<|start_filename|>ext/date/lib/timelib.h<|end_filename|>
/*
   +----------------------------------------------------------------------+
   | PHP Version 5                                                        |
   +----------------------------------------------------------------------+
   | Copyright (c) 1997-2015 The PHP Group                                |
   +----------------------------------------------------------------------+
   | This source file is subject to version 3.01 of the PHP 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.php.net/license/3_01.txt                                  |
   | If you did not receive a copy of the PHP license and are unable to   |
   | obtain it through the world-wide-web, please send a note to          |
   | <EMAIL> so we can mail you a copy immediately.               |
   +----------------------------------------------------------------------+
   | Authors: <NAME> <<EMAIL>>                    |
   +----------------------------------------------------------------------+
 */

/* $Id$ */

#ifndef __TIMELIB_H__
#define __TIMELIB_H__

#include "timelib_structs.h"
#if HAVE_LIMITS_H
#include <limits.h>
#endif

#define TIMELIB_VERSION 201102

#define TIMELIB_NONE             0x00
#define TIMELIB_OVERRIDE_TIME    0x01
#define TIMELIB_NO_CLONE         0x02

#define TIMELIB_UNSET   -99999

#define TIMELIB_SPECIAL_WEEKDAY                   0x01
#define TIMELIB_SPECIAL_DAY_OF_WEEK_IN_MONTH      0x02
#define TIMELIB_SPECIAL_LAST_DAY_OF_WEEK_IN_MONTH 0x03

#define TIMELIB_SPECIAL_FIRST_DAY_OF_MONTH        0x01
#define TIMELIB_SPECIAL_LAST_DAY_OF_MONTH         0x02

#ifndef LONG_MAX
#define LONG_MAX 2147483647L
#endif

#ifndef LONG_MIN
#define LONG_MIN (- LONG_MAX - 1)
#endif

#if defined(_MSC_VER) && !defined(strcasecmp)
#define strcasecmp stricmp
#endif

#if defined(_MSC_VER) && !defined(strncasecmp)
#define strncasecmp strnicmp
#endif

/* Function pointers */
typedef timelib_tzinfo* (*timelib_tz_get_wrapper)(char *tzname, const timelib_tzdb *tzdb);

/* From dow.c */
timelib_sll timelib_day_of_week(timelib_sll y, timelib_sll m, timelib_sll d);
timelib_sll timelib_iso_day_of_week(timelib_sll y, timelib_sll m, timelib_sll d);
timelib_sll timelib_day_of_year(timelib_sll y, timelib_sll m, timelib_sll d);
timelib_sll timelib_daynr_from_weeknr(timelib_sll y, timelib_sll w, timelib_sll d);
timelib_sll timelib_days_in_month(timelib_sll y, timelib_sll m);
void timelib_isoweek_from_date(timelib_sll y, timelib_sll m, timelib_sll d, timelib_sll *iw, timelib_sll *iy);
int timelib_valid_time(timelib_sll h, timelib_sll i, timelib_sll s);
int timelib_valid_date(timelib_sll y, timelib_sll m, timelib_sll d);

/* From parse_date.re */
timelib_time *timelib_strtotime(char *s, int len, timelib_error_container **errors, const timelib_tzdb *tzdb, timelib_tz_get_wrapper tz_get_wrapper);
timelib_time *timelib_parse_from_format(char *format, char *s, int len, timelib_error_container **errors, const timelib_tzdb *tzdb, timelib_tz_get_wrapper tz_get_wrapper);
void timelib_fill_holes(timelib_time *parsed, timelib_time *now, int options);
char *timelib_timezone_id_from_abbr(const char *abbr, long gmtoffset, int isdst);
const timelib_tz_lookup_table *timelib_timezone_abbreviations_list(void);
long timelib_parse_tz_cor(char**);

/* From parse_iso_intervals.re */
void timelib_strtointerval(char *s, int len, 
                           timelib_time **begin, timelib_time **end,
						   timelib_rel_time **period, int *recurrences,
						   struct timelib_error_container **errors);


/* From tm2unixtime.c */
void timelib_update_ts(timelib_time* time, timelib_tzinfo* tzi);
void timelib_do_normalize(timelib_time *base);
void timelib_do_rel_normalize(timelib_time *base, timelib_rel_time *rt);

/* From unixtime2tm.c */
int timelib_apply_localtime(timelib_time *t, unsigned int localtime);
void timelib_unixtime2gmt(timelib_time* tm, timelib_sll ts);
void timelib_unixtime2local(timelib_time *tm, timelib_sll ts);
void timelib_update_from_sse(timelib_time *tm);
void timelib_set_timezone_from_offset(timelib_time *t, timelib_sll utc_offset);
void timelib_set_timezone_from_abbr(timelib_time *t, timelib_abbr_info abbr_info);
void timelib_set_timezone(timelib_time *t, timelib_tzinfo *tz);

/* From parse_tz.c */
int timelib_timezone_id_is_valid(char *timezone, const timelib_tzdb *tzdb);
timelib_tzinfo *timelib_parse_tzfile(char *timezone, const timelib_tzdb *tzdb);
int timelib_timestamp_is_in_dst(timelib_sll ts, timelib_tzinfo *tz);
timelib_time_offset *timelib_get_time_zone_info(timelib_sll ts, timelib_tzinfo *tz);
timelib_sll timelib_get_current_offset(timelib_time *t);
void timelib_dump_tzinfo(timelib_tzinfo *tz);
const timelib_tzdb *timelib_builtin_db(void);
const timelib_tzdb_index_entry *timelib_timezone_builtin_identifiers_list(int *count);
long timelib_parse_zone(char **ptr, int *dst, timelib_time *t, int *tz_not_found, const timelib_tzdb *tzdb, timelib_tz_get_wrapper tz_wrapper);

/* From timelib.c */
timelib_tzinfo* timelib_tzinfo_ctor(char *name);
void timelib_time_tz_abbr_update(timelib_time* tm, char* tz_abbr);
void timelib_time_tz_name_update(timelib_time* tm, char* tz_name);
void timelib_tzinfo_dtor(timelib_tzinfo *tz);
timelib_tzinfo* timelib_tzinfo_clone(timelib_tzinfo *tz);

timelib_rel_time* timelib_rel_time_ctor(void);
void timelib_rel_time_dtor(timelib_rel_time* t);
timelib_rel_time* timelib_rel_time_clone(timelib_rel_time *tz);

timelib_time* timelib_time_ctor(void);
void timelib_time_set_option(timelib_time* tm, int option, void* option_value);
void timelib_time_dtor(timelib_time* t);
timelib_time* timelib_time_clone(timelib_time* orig);

timelib_time_offset* timelib_time_offset_ctor(void);
void timelib_time_offset_dtor(timelib_time_offset* t);

void timelib_error_container_dtor(timelib_error_container *errors);

signed long timelib_date_to_int(timelib_time *d, int *error);
void timelib_dump_date(timelib_time *d, int options);
void timelib_dump_rel_time(timelib_rel_time *d);

void timelib_decimal_hour_to_hms(double h, int *hour, int *min, int *sec);
long timelib_parse_tz_cor(char **ptr);

/* from astro.c */
double timelib_ts_to_juliandate(timelib_sll ts);
int timelib_astro_rise_set_altitude(timelib_time *time, double lon, double lat, double altit, int upper_limb, double *h_rise, double *h_set, timelib_sll *ts_rise, timelib_sll *ts_set, timelib_sll *ts_transit);

/* from interval.c */
timelib_rel_time *timelib_diff(timelib_time *one, timelib_time *two);
timelib_time *timelib_add(timelib_time *t, timelib_rel_time *interval);
timelib_time *timelib_sub(timelib_time *t, timelib_rel_time *interval);

#endif


<|start_filename|>main/win32_internal_function_disabled.h<|end_filename|>
/*
   +----------------------------------------------------------------------+
   | PHP Version 5                                                        |
   +----------------------------------------------------------------------+
   | Copyright (c) 1997-2015 The PHP Group                                |
   +----------------------------------------------------------------------+
   | This source file is subject to version 3.01 of the PHP 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.php.net/license/3_01.txt                                  |
   | If you did not receive a copy of the PHP license and are unable to   |
   | obtain it through the world-wide-web, please send a note to          |
   | <EMAIL> so we can mail you a copy immediately.               |
   +----------------------------------------------------------------------+
   | Author: <NAME> <<EMAIL>>                              |
   +----------------------------------------------------------------------+
 */

/* $Id$ */

/* 5 means the min version is 5 (XP/2000), 6 (2k8/vista), etc. */

/*
Windows Server 2008     6.0
Windows Vista           6.0

Verssions below are not supported anymore, php won't even load:
Windows Server 2003 R2  5.2
Windows Server 2003     5.2
Windows XP              5.1
Windows 2000            5.0
*/
static const char *function_name_5[] = {NULL};
const int function_name_cnt_5 = 0;
static const char *function_name_6[] = {"readlink", "symlink", NULL};
const int function_name_cnt_6 = 2;


<|start_filename|>main/streams/php_stream_filter_api.h<|end_filename|>
/*
   +----------------------------------------------------------------------+
   | PHP Version 5                                                        |
   +----------------------------------------------------------------------+
   | Copyright (c) 1997-2015 The PHP Group                                |
   +----------------------------------------------------------------------+
   | This source file is subject to version 3.01 of the PHP 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.php.net/license/3_01.txt                                  |
   | If you did not receive a copy of the PHP license and are unable to   |
   | obtain it through the world-wide-web, please send a note to          |
   | <EMAIL> so we can mail you a copy immediately.               |
   +----------------------------------------------------------------------+
   | Author: <NAME> <<EMAIL>>                           |
   | With suggestions from:                                               |
   |      <NAME> <<EMAIL>>                     |
   |      <NAME>      <<EMAIL>>                             |
   +----------------------------------------------------------------------+
 */

/* $Id$ */

/* The filter API works on the principle of "Bucket-Brigades".  This is
 * partially inspired by the Apache 2 method of doing things, although
 * it is intentially a light-weight implementation.
 *
 * Each stream can have a chain of filters for reading and another for writing.
 * 
 * When data is written to the stream, it is placed into a bucket and placed at
 * the start of the input brigade.
 *
 * The first filter in the chain is invoked on the brigade and (depending on
 * it's return value), the next filter is invoked and so on.
 * */

#define PHP_STREAM_FILTER_READ	0x0001
#define PHP_STREAM_FILTER_WRITE	0x0002
#define PHP_STREAM_FILTER_ALL	(PHP_STREAM_FILTER_READ | PHP_STREAM_FILTER_WRITE)

typedef struct _php_stream_bucket			php_stream_bucket;
typedef struct _php_stream_bucket_brigade	php_stream_bucket_brigade;

struct _php_stream_bucket {
	php_stream_bucket *next, *prev;
	php_stream_bucket_brigade *brigade;

	char *buf;
	size_t buflen;
	/* if non-zero, buf should be pefreed when the bucket is destroyed */
	int own_buf;
	int is_persistent;
	
	/* destroy this struct when refcount falls to zero */
	int refcount;
};

struct _php_stream_bucket_brigade {
	php_stream_bucket *head, *tail;
};

typedef enum {
	PSFS_ERR_FATAL,	/* error in data stream */
	PSFS_FEED_ME,	/* filter needs more data; stop processing chain until more is available */
	PSFS_PASS_ON	/* filter generated output buckets; pass them on to next in chain */
} php_stream_filter_status_t;

/* Buckets API. */
BEGIN_EXTERN_C()
PHPAPI php_stream_bucket *php_stream_bucket_new(php_stream *stream, char *buf, size_t buflen, int own_buf, int buf_persistent TSRMLS_DC);
PHPAPI int php_stream_bucket_split(php_stream_bucket *in, php_stream_bucket **left, php_stream_bucket **right, size_t length TSRMLS_DC);
PHPAPI void php_stream_bucket_delref(php_stream_bucket *bucket TSRMLS_DC);
#define php_stream_bucket_addref(bucket)	(bucket)->refcount++
PHPAPI void php_stream_bucket_prepend(php_stream_bucket_brigade *brigade, php_stream_bucket *bucket TSRMLS_DC);
PHPAPI void php_stream_bucket_append(php_stream_bucket_brigade *brigade, php_stream_bucket *bucket TSRMLS_DC);
PHPAPI void php_stream_bucket_unlink(php_stream_bucket *bucket TSRMLS_DC);
PHPAPI php_stream_bucket *php_stream_bucket_make_writeable(php_stream_bucket *bucket TSRMLS_DC);
END_EXTERN_C()

#define PSFS_FLAG_NORMAL		0	/* regular read/write */
#define PSFS_FLAG_FLUSH_INC		1	/* an incremental flush */
#define PSFS_FLAG_FLUSH_CLOSE	2	/* final flush prior to closing */

typedef struct _php_stream_filter_ops {

	php_stream_filter_status_t (*filter)(
			php_stream *stream,
			php_stream_filter *thisfilter,
			php_stream_bucket_brigade *buckets_in,
			php_stream_bucket_brigade *buckets_out,
			size_t *bytes_consumed,
			int flags
			TSRMLS_DC);
	
	void (*dtor)(php_stream_filter *thisfilter TSRMLS_DC);
	
	const char *label;
	
} php_stream_filter_ops;

typedef struct _php_stream_filter_chain {
	php_stream_filter *head, *tail;

	/* Owning stream */
	php_stream *stream;
} php_stream_filter_chain;

struct _php_stream_filter {
	php_stream_filter_ops *fops;
	void *abstract; /* for use by filter implementation */
	php_stream_filter *next;
	php_stream_filter *prev;
	int is_persistent;

	/* link into stream and chain */
	php_stream_filter_chain *chain;

	/* buffered buckets */
	php_stream_bucket_brigade buffer;

	/* filters are auto_registered when they're applied */
	int rsrc_id;
};

/* stack filter onto a stream */
BEGIN_EXTERN_C()
PHPAPI void _php_stream_filter_prepend(php_stream_filter_chain *chain, php_stream_filter *filter TSRMLS_DC);
PHPAPI int php_stream_filter_prepend_ex(php_stream_filter_chain *chain, php_stream_filter *filter TSRMLS_DC);
PHPAPI void _php_stream_filter_append(php_stream_filter_chain *chain, php_stream_filter *filter TSRMLS_DC);
PHPAPI int php_stream_filter_append_ex(php_stream_filter_chain *chain, php_stream_filter *filter TSRMLS_DC);
PHPAPI int _php_stream_filter_flush(php_stream_filter *filter, int finish TSRMLS_DC);
PHPAPI php_stream_filter *php_stream_filter_remove(php_stream_filter *filter, int call_dtor TSRMLS_DC);
PHPAPI void php_stream_filter_free(php_stream_filter *filter TSRMLS_DC);
PHPAPI php_stream_filter *_php_stream_filter_alloc(php_stream_filter_ops *fops, void *abstract, int persistent STREAMS_DC TSRMLS_DC);
END_EXTERN_C()
#define php_stream_filter_alloc(fops, thisptr, persistent) _php_stream_filter_alloc((fops), (thisptr), (persistent) STREAMS_CC TSRMLS_CC)
#define php_stream_filter_alloc_rel(fops, thisptr, persistent) _php_stream_filter_alloc((fops), (thisptr), (persistent) STREAMS_REL_CC TSRMLS_CC)
#define php_stream_filter_prepend(chain, filter) _php_stream_filter_prepend((chain), (filter) TSRMLS_CC)
#define php_stream_filter_append(chain, filter) _php_stream_filter_append((chain), (filter) TSRMLS_CC)
#define php_stream_filter_flush(filter, finish) _php_stream_filter_flush((filter), (finish) TSRMLS_CC)

#define php_stream_is_filtered(stream)	((stream)->readfilters.head || (stream)->writefilters.head)

typedef struct _php_stream_filter_factory {
	php_stream_filter *(*create_filter)(const char *filtername, zval *filterparams, int persistent TSRMLS_DC);
} php_stream_filter_factory;

BEGIN_EXTERN_C()
PHPAPI int php_stream_filter_register_factory(const char *filterpattern, php_stream_filter_factory *factory TSRMLS_DC);
PHPAPI int php_stream_filter_unregister_factory(const char *filterpattern TSRMLS_DC);
PHPAPI int php_stream_filter_register_factory_volatile(const char *filterpattern, php_stream_filter_factory *factory TSRMLS_DC);
PHPAPI php_stream_filter *php_stream_filter_create(const char *filtername, zval *filterparams, int persistent TSRMLS_DC);
END_EXTERN_C()

/*
 * Local variables:
 * tab-width: 4
 * c-basic-offset: 4
 * End:
 * vim600: sw=4 ts=4 fdm=marker
 * vim<600: sw=4 ts=4
 */


<|start_filename|>ext/mysqlnd/mysql_float_to_double.h<|end_filename|>
/*
  +----------------------------------------------------------------------+
  | PHP Version 5                                                        |
  +----------------------------------------------------------------------+
  | Copyright (c) 2006-2014 The PHP Group                                |
  +----------------------------------------------------------------------+
  | This source file is subject to version 3.01 of the PHP 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.php.net/license/3_01.txt                                  |
  | If you did not receive a copy of the PHP license and are unable to   |
  | obtain it through the world-wide-web, please send a note to          |
  | <EMAIL> so we can mail you a copy immediately.               |
  +----------------------------------------------------------------------+
  | Authors: <NAME> <<EMAIL>>                          |
  +----------------------------------------------------------------------+
*/

#ifndef MYSQL_FLOAT_TO_DOUBLE_H
#define MYSQL_FLOAT_TO_DOUBLE_H

#include "main/php.h"
#include <float.h>
#include "main/snprintf.h"

#define MAX_CHAR_BUF_LEN 255

#ifndef FLT_DIG
# define FLT_DIG 6
#endif

/*
 * Convert from a 4-byte float to a 8-byte decimal by first converting
 * the float to a string, and then the string to a double.
 * The decimals argument specifies the precision of the output. If decimals
 * is less than zero, then a gcvt(3) like logic is used with the significant
 * digits set to FLT_DIG i.e. 6.
 */
static inline double mysql_float_to_double(float fp4, int decimals) {
	char num_buf[MAX_CHAR_BUF_LEN]; /* Over allocated */

	if (decimals < 0) {
		php_gcvt(fp4, FLT_DIG, '.', 'e', num_buf);
	} else {
		php_sprintf(num_buf, "%.*f", decimals, fp4);
	}

	return zend_strtod(num_buf, NULL);
}

/*
 * Local variables:
 * tab-width: 4
 * c-basic-offset: 4
 * End:
 * vim600: noet sw=4 ts=4 fdm=marker
 * vim<600: noet sw=4 ts=4
 */

#endif /* MYSQL_FLOAT_TO_DOUBLE_H */


<|start_filename|>Zend/zend_interfaces.c<|end_filename|>
/*
   +----------------------------------------------------------------------+
   | Zend Engine                                                          |
   +----------------------------------------------------------------------+
   | Copyright (c) 1998-2015 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          |
   | <EMAIL> so we can mail you a copy immediately.              |
   +----------------------------------------------------------------------+
   | Authors: <NAME> <<EMAIL>>                              |
   +----------------------------------------------------------------------+
*/

/* $Id$ */

#include "zend.h"
#include "zend_API.h"
#include "zend_interfaces.h"
#include "zend_exceptions.h"

ZEND_API zend_class_entry *zend_ce_traversable;
ZEND_API zend_class_entry *zend_ce_aggregate;
ZEND_API zend_class_entry *zend_ce_iterator;
ZEND_API zend_class_entry *zend_ce_arrayaccess;
ZEND_API zend_class_entry *zend_ce_serializable;

/* {{{ zend_call_method
 Only returns the returned zval if retval_ptr != NULL */
ZEND_API zval* zend_call_method(zval **object_pp, zend_class_entry *obj_ce, zend_function **fn_proxy, const char *function_name, int function_name_len, zval **retval_ptr_ptr, int param_count, zval* arg1, zval* arg2 TSRMLS_DC)
{
	int result;
	zend_fcall_info fci;
	zval z_fname;
	zval *retval;
	HashTable *function_table;

	zval **params[2];

	params[0] = &arg1;
	params[1] = &arg2;

	fci.size = sizeof(fci);
	/*fci.function_table = NULL; will be read form zend_class_entry of object if needed */
	fci.object_ptr = object_pp ? *object_pp : NULL;
	fci.function_name = &z_fname;
	fci.retval_ptr_ptr = retval_ptr_ptr ? retval_ptr_ptr : &retval;
	fci.param_count = param_count;
	fci.params = params;
	fci.no_separation = 1;
	fci.symbol_table = NULL;

	if (!fn_proxy && !obj_ce) {
		/* no interest in caching and no information already present that is
		 * needed later inside zend_call_function. */
		ZVAL_STRINGL(&z_fname, function_name, function_name_len, 0);
		fci.function_table = !object_pp ? EG(function_table) : NULL;
		result = zend_call_function(&fci, NULL TSRMLS_CC);
	} else {
		zend_fcall_info_cache fcic;

		fcic.initialized = 1;
		if (!obj_ce) {
			obj_ce = object_pp ? Z_OBJCE_PP(object_pp) : NULL;
		}
		if (obj_ce) {
			function_table = &obj_ce->function_table;
		} else {
			function_table = EG(function_table);
		}
		if (!fn_proxy || !*fn_proxy) {
			if (zend_hash_find(function_table, function_name, function_name_len+1, (void **) &fcic.function_handler) == FAILURE) {
				/* error at c-level */
				zend_error(E_CORE_ERROR, "Couldn't find implementation for method %s%s%s", obj_ce ? obj_ce->name : "", obj_ce ? "::" : "", function_name);
			}
			if (fn_proxy) {
				*fn_proxy = fcic.function_handler;
			}
		} else {
			fcic.function_handler = *fn_proxy;
		}
		fcic.calling_scope = obj_ce;
		if (object_pp) {
			fcic.called_scope = Z_OBJCE_PP(object_pp);
		} else if (obj_ce &&
		           !(EG(called_scope) &&
		             instanceof_function(EG(called_scope), obj_ce TSRMLS_CC))) {
			fcic.called_scope = obj_ce;
		} else {
			fcic.called_scope = EG(called_scope);
		}
		fcic.object_ptr = object_pp ? *object_pp : NULL;
		result = zend_call_function(&fci, &fcic TSRMLS_CC);
	}
	if (result == FAILURE) {
		/* error at c-level */
		if (!obj_ce) {
			obj_ce = object_pp ? Z_OBJCE_PP(object_pp) : NULL;
		}
		if (!EG(exception)) {
			zend_error(E_CORE_ERROR, "Couldn't execute method %s%s%s", obj_ce ? obj_ce->name : "", obj_ce ? "::" : "", function_name);
		}
	}
	if (!retval_ptr_ptr) {
		if (retval) {
			zval_ptr_dtor(&retval);
		}
		return NULL;
	}
	return *retval_ptr_ptr;
}
/* }}} */

/* iterator interface, c-level functions used by engine */

/* {{{ zend_user_it_new_iterator */
ZEND_API zval *zend_user_it_new_iterator(zend_class_entry *ce, zval *object TSRMLS_DC)
{
	zval *retval;

	return zend_call_method_with_0_params(&object, ce, &ce->iterator_funcs.zf_new_iterator, "getiterator", &retval);

}
/* }}} */

/* {{{ zend_user_it_invalidate_current */
ZEND_API void zend_user_it_invalidate_current(zend_object_iterator *_iter TSRMLS_DC)
{
	zend_user_iterator *iter = (zend_user_iterator*)_iter;

	if (iter->value) {
		zval_ptr_dtor(&iter->value);
		iter->value = NULL;
	}
}
/* }}} */

/* {{{ zend_user_it_dtor */
static void zend_user_it_dtor(zend_object_iterator *_iter TSRMLS_DC)
{
	zend_user_iterator *iter = (zend_user_iterator*)_iter;
	zval *object = (zval*)iter->it.data;

	zend_user_it_invalidate_current(_iter TSRMLS_CC);
	zval_ptr_dtor(&object);
	efree(iter);
}
/* }}} */

/* {{{ zend_user_it_valid */
ZEND_API int zend_user_it_valid(zend_object_iterator *_iter TSRMLS_DC)
{
	if (_iter) {
		zend_user_iterator *iter = (zend_user_iterator*)_iter;
		zval *object = (zval*)iter->it.data;
		zval *more;
		int result;

		zend_call_method_with_0_params(&object, iter->ce, &iter->ce->iterator_funcs.zf_valid, "valid", &more);
		if (more) {
			result = i_zend_is_true(more);
			zval_ptr_dtor(&more);
			return result ? SUCCESS : FAILURE;
		}
	}
	return FAILURE;
}
/* }}} */

/* {{{ zend_user_it_get_current_data */
ZEND_API void zend_user_it_get_current_data(zend_object_iterator *_iter, zval ***data TSRMLS_DC)
{
	zend_user_iterator *iter = (zend_user_iterator*)_iter;
	zval *object = (zval*)iter->it.data;

	if (!iter->value) {
		zend_call_method_with_0_params(&object, iter->ce, &iter->ce->iterator_funcs.zf_current, "current", &iter->value);
	}
	*data = &iter->value;
}
/* }}} */

/* {{{ zend_user_it_get_current_key_default */
#if 0
static int zend_user_it_get_current_key_default(zend_object_iterator *_iter, char **str_key, uint *str_key_len, ulong *int_key TSRMLS_DC)
{
	*int_key = _iter->index;
	return HASH_KEY_IS_LONG;
}
#endif
/* }}} */

/* {{{ zend_user_it_get_current_key */
ZEND_API void zend_user_it_get_current_key(zend_object_iterator *_iter, zval *key TSRMLS_DC)
{
	zend_user_iterator *iter = (zend_user_iterator*)_iter;
	zval *object = (zval*)iter->it.data;
	zval *retval;

	zend_call_method_with_0_params(&object, iter->ce, &iter->ce->iterator_funcs.zf_key, "key", &retval);

	if (retval) {
		ZVAL_ZVAL(key, retval, 1, 1);
	} else {
		if (!EG(exception)) {
			zend_error(E_WARNING, "Nothing returned from %s::key()", iter->ce->name);
		}

		ZVAL_LONG(key, 0);
	}
}

/* {{{ zend_user_it_move_forward */
ZEND_API void zend_user_it_move_forward(zend_object_iterator *_iter TSRMLS_DC)
{
	zend_user_iterator *iter = (zend_user_iterator*)_iter;
	zval *object = (zval*)iter->it.data;

	zend_user_it_invalidate_current(_iter TSRMLS_CC);
	zend_call_method_with_0_params(&object, iter->ce, &iter->ce->iterator_funcs.zf_next, "next", NULL);
}
/* }}} */

/* {{{ zend_user_it_rewind */
ZEND_API void zend_user_it_rewind(zend_object_iterator *_iter TSRMLS_DC)
{
	zend_user_iterator *iter = (zend_user_iterator*)_iter;
	zval *object = (zval*)iter->it.data;

	zend_user_it_invalidate_current(_iter TSRMLS_CC);
	zend_call_method_with_0_params(&object, iter->ce, &iter->ce->iterator_funcs.zf_rewind, "rewind", NULL);
}
/* }}} */

zend_object_iterator_funcs zend_interface_iterator_funcs_iterator = {
	zend_user_it_dtor,
	zend_user_it_valid,
	zend_user_it_get_current_data,
	zend_user_it_get_current_key,
	zend_user_it_move_forward,
	zend_user_it_rewind,
	zend_user_it_invalidate_current
};

/* {{{ zend_user_it_get_iterator */
static zend_object_iterator *zend_user_it_get_iterator(zend_class_entry *ce, zval *object, int by_ref TSRMLS_DC)
{
	zend_user_iterator *iterator;

	if (by_ref) {
		zend_error(E_ERROR, "An iterator cannot be used with foreach by reference");
	}

	iterator = emalloc(sizeof(zend_user_iterator));

	Z_ADDREF_P(object);
	iterator->it.data = (void*)object;
	iterator->it.funcs = ce->iterator_funcs.funcs;
	iterator->ce = Z_OBJCE_P(object);
	iterator->value = NULL;
	return (zend_object_iterator*)iterator;
}
/* }}} */

/* {{{ zend_user_it_get_new_iterator */
ZEND_API zend_object_iterator *zend_user_it_get_new_iterator(zend_class_entry *ce, zval *object, int by_ref TSRMLS_DC)
{
	zval *iterator = zend_user_it_new_iterator(ce, object TSRMLS_CC);
	zend_object_iterator *new_iterator;

	zend_class_entry *ce_it = iterator && Z_TYPE_P(iterator) == IS_OBJECT ? Z_OBJCE_P(iterator) : NULL;

	if (!ce_it || !ce_it->get_iterator || (ce_it->get_iterator == zend_user_it_get_new_iterator && iterator == object)) {
		if (!EG(exception)) {
			zend_throw_exception_ex(NULL, 0 TSRMLS_CC, "Objects returned by %s::getIterator() must be traversable or implement interface Iterator", ce ? ce->name : Z_OBJCE_P(object)->name);
		}
		if (iterator) {
			zval_ptr_dtor(&iterator);
		}
		return NULL;
	}

	new_iterator = ce_it->get_iterator(ce_it, iterator, by_ref TSRMLS_CC);
	zval_ptr_dtor(&iterator);
	return new_iterator;
}
/* }}} */

/* {{{ zend_implement_traversable */
static int zend_implement_traversable(zend_class_entry *interface, zend_class_entry *class_type TSRMLS_DC)
{
	/* check that class_type is traversable at c-level or implements at least one of 'aggregate' and 'Iterator' */
	zend_uint i;

	if (class_type->get_iterator || (class_type->parent && class_type->parent->get_iterator)) {
		return SUCCESS;
	}
	for (i = 0; i < class_type->num_interfaces; i++) {
		if (class_type->interfaces[i] == zend_ce_aggregate || class_type->interfaces[i] == zend_ce_iterator) {
			return SUCCESS;
		}
	}
	zend_error(E_CORE_ERROR, "Class %s must implement interface %s as part of either %s or %s",
		class_type->name,
		zend_ce_traversable->name,
		zend_ce_iterator->name,
		zend_ce_aggregate->name);
	return FAILURE;
}
/* }}} */

/* {{{ zend_implement_aggregate */
static int zend_implement_aggregate(zend_class_entry *interface, zend_class_entry *class_type TSRMLS_DC)
{
	int i, t = -1;

	if (class_type->get_iterator) {
		if (class_type->type == ZEND_INTERNAL_CLASS) {
			/* inheritance ensures the class has necessary userland methods */
			return SUCCESS;
		} else if (class_type->get_iterator != zend_user_it_get_new_iterator) {
			/* c-level get_iterator cannot be changed (exception being only Traversable is implmented) */
			if (class_type->num_interfaces) {
				for (i = 0; i < class_type->num_interfaces; i++) {
					if (class_type->interfaces[i] == zend_ce_iterator) {
						zend_error(E_ERROR, "Class %s cannot implement both %s and %s at the same time",
									class_type->name,
									interface->name,
									zend_ce_iterator->name);
						return FAILURE;
					}
					if (class_type->interfaces[i] == zend_ce_traversable) {
						t = i;
					}
				}
			}
			if (t == -1) {
				return FAILURE;
			}
		}
	}
	class_type->iterator_funcs.zf_new_iterator = NULL;
	class_type->get_iterator = zend_user_it_get_new_iterator;
	return SUCCESS;
}
/* }}} */

/* {{{ zend_implement_iterator */
static int zend_implement_iterator(zend_class_entry *interface, zend_class_entry *class_type TSRMLS_DC)
{
	if (class_type->get_iterator && class_type->get_iterator != zend_user_it_get_iterator) {
		if (class_type->type == ZEND_INTERNAL_CLASS) {
			/* inheritance ensures the class has the necessary userland methods */
			return SUCCESS;
		} else {
			/* c-level get_iterator cannot be changed */
			if (class_type->get_iterator == zend_user_it_get_new_iterator) {
				zend_error(E_ERROR, "Class %s cannot implement both %s and %s at the same time",
							class_type->name,
							interface->name,
							zend_ce_aggregate->name);
			}
			return FAILURE;
		}
	}
	class_type->get_iterator = zend_user_it_get_iterator;
	class_type->iterator_funcs.zf_valid = NULL;
	class_type->iterator_funcs.zf_current = NULL;
	class_type->iterator_funcs.zf_key = NULL;
	class_type->iterator_funcs.zf_next = NULL;
	class_type->iterator_funcs.zf_rewind = NULL;
	if (!class_type->iterator_funcs.funcs) {
		class_type->iterator_funcs.funcs = &zend_interface_iterator_funcs_iterator;
	}
	return SUCCESS;
}
/* }}} */

/* {{{ zend_implement_arrayaccess */
static int zend_implement_arrayaccess(zend_class_entry *interface, zend_class_entry *class_type TSRMLS_DC)
{
#if 0
	/* get ht from ce */
	if (ht->read_dimension != zend_std_read_dimension
	||  ht->write_dimension != zend_std_write_dimension
	||  ht->has_dimension != zend_std_has_dimension
	||  ht->unset_dimension != zend_std_unset_dimension) {
		return FAILURE;
	}
#endif
	return SUCCESS;
}
/* }}}*/

/* {{{ zend_user_serialize */
ZEND_API int zend_user_serialize(zval *object, unsigned char **buffer, zend_uint *buf_len, zend_serialize_data *data TSRMLS_DC)
{
	zend_class_entry * ce = Z_OBJCE_P(object);
	zval *retval;
	int result;

	zend_call_method_with_0_params(&object, ce, &ce->serialize_func, "serialize", &retval);


	if (!retval || EG(exception)) {
		result = FAILURE;
	} else {
		switch(Z_TYPE_P(retval)) {
		case IS_NULL:
			/* we could also make this '*buf_len = 0' but this allows to skip variables */
			zval_ptr_dtor(&retval);
			return FAILURE;
		case IS_STRING:
			*buffer = (unsigned char*)estrndup(Z_STRVAL_P(retval), Z_STRLEN_P(retval));
			*buf_len = Z_STRLEN_P(retval);
			result = SUCCESS;
			break;
		default: /* failure */
			result = FAILURE;
			break;
		}
		zval_ptr_dtor(&retval);
	}

	if (result == FAILURE && !EG(exception)) {
		zend_throw_exception_ex(NULL, 0 TSRMLS_CC, "%s::serialize() must return a string or NULL", ce->name);
	}
	return result;
}
/* }}} */

/* {{{ zend_user_unserialize */
ZEND_API int zend_user_unserialize(zval **object, zend_class_entry *ce, const unsigned char *buf, zend_uint buf_len, zend_unserialize_data *data TSRMLS_DC)
{
	zval * zdata;

	object_init_ex(*object, ce);

	MAKE_STD_ZVAL(zdata);
	ZVAL_STRINGL(zdata, (char*)buf, buf_len, 1);

	zend_call_method_with_1_params(object, ce, &ce->unserialize_func, "unserialize", NULL, zdata);

	zval_ptr_dtor(&zdata);

	if (EG(exception)) {
		return FAILURE;
	} else {
		return SUCCESS;
	}
}
/* }}} */

ZEND_API int zend_class_serialize_deny(zval *object, unsigned char **buffer, zend_uint *buf_len, zend_serialize_data *data TSRMLS_DC) /* {{{ */
{
	zend_class_entry *ce = Z_OBJCE_P(object);
	zend_throw_exception_ex(NULL, 0 TSRMLS_CC, "Serialization of '%s' is not allowed", ce->name);
	return FAILURE;
}
/* }}} */

ZEND_API int zend_class_unserialize_deny(zval **object, zend_class_entry *ce, const unsigned char *buf, zend_uint buf_len, zend_unserialize_data *data TSRMLS_DC) /* {{{ */
{
	zend_throw_exception_ex(NULL, 0 TSRMLS_CC, "Unserialization of '%s' is not allowed", ce->name);
	return FAILURE;
}
/* }}} */

/* {{{ zend_implement_serializable */
static int zend_implement_serializable(zend_class_entry *interface, zend_class_entry *class_type TSRMLS_DC)
{
	if (class_type->parent
		&& (class_type->parent->serialize || class_type->parent->unserialize)
		&& !instanceof_function_ex(class_type->parent, zend_ce_serializable, 1 TSRMLS_CC)) {
		return FAILURE;
	}
	if (!class_type->serialize) {
		class_type->serialize = zend_user_serialize;
	}
	if (!class_type->unserialize) {
		class_type->unserialize = zend_user_unserialize;
	}
	return SUCCESS;
}
/* }}}*/

/* {{{ function tables */
const zend_function_entry zend_funcs_aggregate[] = {
	ZEND_ABSTRACT_ME(iterator, getIterator, NULL)
	{NULL, NULL, NULL}
};

const zend_function_entry zend_funcs_iterator[] = {
	ZEND_ABSTRACT_ME(iterator, current,  NULL)
	ZEND_ABSTRACT_ME(iterator, next,     NULL)
	ZEND_ABSTRACT_ME(iterator, key,      NULL)
	ZEND_ABSTRACT_ME(iterator, valid,    NULL)
	ZEND_ABSTRACT_ME(iterator, rewind,   NULL)
	{NULL, NULL, NULL}
};

const zend_function_entry *zend_funcs_traversable    = NULL;

ZEND_BEGIN_ARG_INFO_EX(arginfo_arrayaccess_offset, 0, 0, 1)
	ZEND_ARG_INFO(0, offset)
ZEND_END_ARG_INFO()

ZEND_BEGIN_ARG_INFO_EX(arginfo_arrayaccess_offset_get, 0, 0, 1) /* actually this should be return by ref but atm cannot be */
	ZEND_ARG_INFO(0, offset)
ZEND_END_ARG_INFO()

ZEND_BEGIN_ARG_INFO_EX(arginfo_arrayaccess_offset_value, 0, 0, 2)
	ZEND_ARG_INFO(0, offset)
	ZEND_ARG_INFO(0, value)
ZEND_END_ARG_INFO()

const zend_function_entry zend_funcs_arrayaccess[] = {
	ZEND_ABSTRACT_ME(arrayaccess, offsetExists, arginfo_arrayaccess_offset)
	ZEND_ABSTRACT_ME(arrayaccess, offsetGet,    arginfo_arrayaccess_offset_get)
	ZEND_ABSTRACT_ME(arrayaccess, offsetSet,    arginfo_arrayaccess_offset_value)
	ZEND_ABSTRACT_ME(arrayaccess, offsetUnset,  arginfo_arrayaccess_offset)
	{NULL, NULL, NULL}
};

ZEND_BEGIN_ARG_INFO(arginfo_serializable_serialize, 0)
	ZEND_ARG_INFO(0, serialized)
ZEND_END_ARG_INFO()

const zend_function_entry zend_funcs_serializable[] = {
	ZEND_ABSTRACT_ME(serializable, serialize,   NULL)
	ZEND_FENTRY(unserialize, NULL, arginfo_serializable_serialize, ZEND_ACC_PUBLIC|ZEND_ACC_ABSTRACT|ZEND_ACC_CTOR)
	{NULL, NULL, NULL}
};
/* }}} */

#define REGISTER_ITERATOR_INTERFACE(class_name, class_name_str) \
	{\
		zend_class_entry ce;\
		INIT_CLASS_ENTRY(ce, # class_name_str, zend_funcs_ ## class_name) \
		zend_ce_ ## class_name = zend_register_internal_interface(&ce TSRMLS_CC);\
		zend_ce_ ## class_name->interface_gets_implemented = zend_implement_ ## class_name;\
	}

#define REGISTER_ITERATOR_IMPLEMENT(class_name, interface_name) \
	zend_class_implements(zend_ce_ ## class_name TSRMLS_CC, 1, zend_ce_ ## interface_name)

/* {{{ zend_register_interfaces */
ZEND_API void zend_register_interfaces(TSRMLS_D)
{
	REGISTER_ITERATOR_INTERFACE(traversable, Traversable);

	REGISTER_ITERATOR_INTERFACE(aggregate, IteratorAggregate);
	REGISTER_ITERATOR_IMPLEMENT(aggregate, traversable);

	REGISTER_ITERATOR_INTERFACE(iterator, Iterator);
	REGISTER_ITERATOR_IMPLEMENT(iterator, traversable);

	REGISTER_ITERATOR_INTERFACE(arrayaccess, ArrayAccess);

	REGISTER_ITERATOR_INTERFACE(serializable, Serializable)
}
/* }}} */

/*
 * Local variables:
 * tab-width: 4
 * c-basic-offset: 4
 * indent-tabs-mode: t
 * End:
 */


<|start_filename|>ext/sockets/sockaddr_conv.c<|end_filename|>
#include <php.h>
#include <php_network.h>
#include "php_sockets.h"

#ifdef PHP_WIN32
#include "windows_common.h"
#else
#include <netdb.h>
#include <arpa/inet.h>
#endif

extern int php_string_to_if_index(const char *val, unsigned *out TSRMLS_DC);

#if HAVE_IPV6
/* Sets addr by hostname, or by ip in string form (AF_INET6) */
int php_set_inet6_addr(struct sockaddr_in6 *sin6, char *string, php_socket *php_sock TSRMLS_DC) /* {{{ */
{
	struct in6_addr tmp;
#if HAVE_GETADDRINFO
	struct addrinfo hints;
	struct addrinfo *addrinfo = NULL;
#endif
	char *scope = strchr(string, '%');

	if (inet_pton(AF_INET6, string, &tmp)) {
		memcpy(&(sin6->sin6_addr.s6_addr), &(tmp.s6_addr), sizeof(struct in6_addr));
	} else {
#if HAVE_GETADDRINFO

		memset(&hints, 0, sizeof(struct addrinfo));
		hints.ai_family = AF_INET6;
#if HAVE_AI_V4MAPPED
		hints.ai_flags = AI_V4MAPPED | AI_ADDRCONFIG;
#else
		hints.ai_flags = AI_ADDRCONFIG;
#endif
		getaddrinfo(string, NULL, &hints, &addrinfo);
		if (!addrinfo) {
#ifdef PHP_WIN32
			PHP_SOCKET_ERROR(php_sock, "Host lookup failed", WSAGetLastError());
#else
			PHP_SOCKET_ERROR(php_sock, "Host lookup failed", (-10000 - h_errno));
#endif
			return 0;
		}
		if (addrinfo->ai_family != PF_INET6 || addrinfo->ai_addrlen != sizeof(struct sockaddr_in6)) {
			php_error_docref(NULL TSRMLS_CC, E_WARNING, "Host lookup failed: Non AF_INET6 domain returned on AF_INET6 socket");
			freeaddrinfo(addrinfo);
			return 0;
		}

		memcpy(&(sin6->sin6_addr.s6_addr), ((struct sockaddr_in6*)(addrinfo->ai_addr))->sin6_addr.s6_addr, sizeof(struct in6_addr));
		freeaddrinfo(addrinfo);

#else
		/* No IPv6 specific hostname resolution is available on this system? */
		php_error_docref(NULL TSRMLS_CC, E_WARNING, "Host lookup failed: getaddrinfo() not available on this system");
		return 0;
#endif

	}

	if (scope++) {
		long lval = 0;
		double dval = 0;
		unsigned scope_id = 0;

		if (IS_LONG == is_numeric_string(scope, strlen(scope), &lval, &dval, 0)) {
			if (lval > 0 && lval <= UINT_MAX) {
				scope_id = lval;
			}
		} else {
			php_string_to_if_index(scope, &scope_id TSRMLS_CC);
		}

		sin6->sin6_scope_id = scope_id;
	}

	return 1;
}
/* }}} */
#endif

/* Sets addr by hostname, or by ip in string form (AF_INET)  */
int php_set_inet_addr(struct sockaddr_in *sin, char *string, php_socket *php_sock TSRMLS_DC) /* {{{ */
{
	struct in_addr tmp;
	struct hostent *host_entry;

	if (inet_aton(string, &tmp)) {
		sin->sin_addr.s_addr = tmp.s_addr;
	} else {
		if (strlen(string) > MAXFQDNLEN || ! (host_entry = gethostbyname(string))) {
			/* Note: < -10000 indicates a host lookup error */
#ifdef PHP_WIN32
			PHP_SOCKET_ERROR(php_sock, "Host lookup failed", WSAGetLastError());
#else
			PHP_SOCKET_ERROR(php_sock, "Host lookup failed", (-10000 - h_errno));
#endif
			return 0;
		}
		if (host_entry->h_addrtype != AF_INET) {
			php_error_docref(NULL TSRMLS_CC, E_WARNING, "Host lookup failed: Non AF_INET domain returned on AF_INET socket");
			return 0;
		}
		memcpy(&(sin->sin_addr.s_addr), host_entry->h_addr_list[0], host_entry->h_length);
	}

	return 1;
}
/* }}} */

/* Sets addr by hostname or by ip in string form (AF_INET or AF_INET6,
 * depending on the socket) */
int php_set_inet46_addr(php_sockaddr_storage *ss, socklen_t *ss_len, char *string, php_socket *php_sock TSRMLS_DC) /* {{{ */
{
	if (php_sock->type == AF_INET) {
		struct sockaddr_in t = {0};
		if (php_set_inet_addr(&t, string, php_sock TSRMLS_CC)) {
			memcpy(ss, &t, sizeof t);
			ss->ss_family = AF_INET;
			*ss_len = sizeof(t);
			return 1;
		}
	}
#if HAVE_IPV6
	else if (php_sock->type == AF_INET6) {
		struct sockaddr_in6 t = {0};
		if (php_set_inet6_addr(&t, string, php_sock TSRMLS_CC)) {
			memcpy(ss, &t, sizeof t);
			ss->ss_family = AF_INET6;
			*ss_len = sizeof(t);
			return 1;
		}
	}
#endif
	else {
		php_error_docref(NULL TSRMLS_CC, E_WARNING,
			"IP address used in the context of an unexpected type of socket");
	}
	return 0;
}


<|start_filename|>ext/date/lib/timezonemap.h<|end_filename|>
	{ "acdt",  1,  37800, "Australia/Adelaide"            },
	{ "acdt",  1,  37800, "Australia/Broken_Hill"         },
	{ "acdt",  1,  37800, "Australia/Darwin"              },
	{ "acdt",  1,  37800, "Australia/North"               },
	{ "acdt",  1,  37800, "Australia/South"               },
	{ "acdt",  1,  37800, "Australia/Yancowinna"          },
	{ "acst",  1, -14400, "America/Porto_Acre"            },
	{ "acst",  0,  34200, "Australia/Adelaide"            },
	{ "acst",  1, -14400, "America/Eirunepe"              },
	{ "acst",  1, -14400, "America/Rio_Branco"            },
	{ "acst",  1, -14400, "Brazil/Acre"                   },
	{ "acst",  0,  34200, "Asia/Jayapura"                 },
	{ "acst",  0,  34200, "Australia/Broken_Hill"         },
	{ "acst",  0,  34200, "Australia/Darwin"              },
	{ "acst",  0,  34200, "Australia/North"               },
	{ "acst",  0,  34200, "Australia/South"               },
	{ "acst",  0,  34200, "Australia/Yancowinna"          },
	{ "act",   0, -18000, "America/Porto_Acre"            },
	{ "act",   0, -18000, "America/Eirunepe"              },
	{ "act",   0, -18000, "America/Rio_Branco"            },
	{ "act",   0, -18000, "Brazil/Acre"                   },
	{ "acwdt", 1,  35100, "Australia/Eucla"               },
	{ "acwst", 0,  31500, "Australia/Eucla"               },
	{ "addt",  1,  -7200, "America/Goose_Bay"             },
	{ "addt",  1,  -7200, "America/Pangnirtung"           },
	{ "admt",  0,   9320, "Africa/Addis_Ababa"            },
	{ "admt",  0,   9320, "Africa/Asmara"                 },
	{ "admt",  0,   9320, "Africa/Asmera"                 },
	{ "adt",   1, -10800, "America/Halifax"               },
	{ "adt",   1, -10800, "America/Barbados"              },
	{ "adt",   1, -10800, "America/Blanc-Sablon"          },
	{ "adt",   1, -10800, "America/Glace_Bay"             },
	{ "adt",   1, -10800, "America/Goose_Bay"             },
	{ "adt",   1, -10800, "America/Martinique"            },
	{ "adt",   1, -10800, "America/Moncton"               },
	{ "adt",   1, -10800, "America/Pangnirtung"           },
	{ "adt",   1, -10800, "America/Thule"                 },
	{ "adt",   1, -10800, "Atlantic/Bermuda"              },
	{ "adt",   1, -10800, "Canada/Atlantic"               },
	{ "adt",   1,  14400, "Asia/Baghdad"                  },
	{ "aedt",  1,  39600, "Australia/Melbourne"           },
	{ "aedt",  1,  39600, "Antarctica/Macquarie"          },
	{ "aedt",  1,  39600, "Australia/ACT"                 },
	{ "aedt",  1,  39600, "Australia/Brisbane"            },
	{ "aedt",  1,  39600, "Australia/Canberra"            },
	{ "aedt",  1,  39600, "Australia/Currie"              },
	{ "aedt",  1,  39600, "Australia/Hobart"              },
	{ "aedt",  1,  39600, "Australia/Lindeman"            },
	{ "aedt",  1,  39600, "Australia/NSW"                 },
	{ "aedt",  1,  39600, "Australia/Queensland"          },
	{ "aedt",  1,  39600, "Australia/Sydney"              },
	{ "aedt",  1,  39600, "Australia/Tasmania"            },
	{ "aedt",  1,  39600, "Australia/Victoria"            },
	{ "aest",  0,  36000, "Australia/Melbourne"           },
	{ "aest",  0,  36000, "Antarctica/Macquarie"          },
	{ "aest",  0,  36000, "Australia/ACT"                 },
	{ "aest",  0,  36000, "Australia/Brisbane"            },
	{ "aest",  0,  36000, "Australia/Canberra"            },
	{ "aest",  0,  36000, "Australia/Currie"              },
	{ "aest",  0,  36000, "Australia/Hobart"              },
	{ "aest",  0,  36000, "Australia/LHI"                 },
	{ "aest",  0,  36000, "Australia/Lindeman"            },
	{ "aest",  0,  36000, "Australia/Lord_Howe"           },
	{ "aest",  0,  36000, "Australia/NSW"                 },
	{ "aest",  0,  36000, "Australia/Queensland"          },
	{ "aest",  0,  36000, "Australia/Sydney"              },
	{ "aest",  0,  36000, "Australia/Tasmania"            },
	{ "aest",  0,  36000, "Australia/Victoria"            },
	{ "aft",   0,  16200, "Asia/Kabul"                    },
	{ "aft",   0,  14400, "Asia/Kabul"                    },
	{ "ahdt",  1, -32400, "America/Anchorage"             },
	{ "ahst",  0, -36000, "America/Anchorage"             },
	{ "ahst",  0, -36000, "America/Adak"                  },
	{ "ahst",  0, -36000, "America/Atka"                  },
	{ "akdt",  1, -28800, "America/Anchorage"             },
	{ "akdt",  1, -28800, "America/Juneau"                },
	{ "akdt",  1, -28800, "America/Nome"                  },
	{ "akdt",  1, -28800, "America/Sitka"                 },
	{ "akdt",  1, -28800, "America/Yakutat"               },
	{ "akst",  0, -32400, "America/Anchorage"             },
	{ "akst",  0, -32400, "America/Juneau"                },
	{ "akst",  0, -32400, "America/Nome"                  },
	{ "akst",  0, -32400, "America/Sitka"                 },
	{ "akst",  0, -32400, "America/Yakutat"               },
	{ "aktst", 1,  21600, "Asia/Aqtobe"                   },
	{ "aktt",  0,  21600, "Asia/Aqtobe"                   },
	{ "aktt",  0,  14400, "Asia/Aqtobe"                   },
	{ "aktt",  0,  18000, "Asia/Aqtobe"                   },
	{ "almst", 1,  25200, "Asia/Almaty"                   },
	{ "almt",  0,  21600, "Asia/Almaty"                   },
	{ "almt",  0,  18000, "Asia/Almaty"                   },
	{ "amst",  1,  18000, "Asia/Yerevan"                  },
	{ "amst",  1,  14400, "Asia/Yerevan"                  },
	{ "amst",  1, -10800, "America/Boa_Vista"             },
	{ "amst",  1, -10800, "America/Campo_Grande"          },
	{ "amst",  1, -10800, "America/Cuiaba"                },
	{ "amst",  1, -10800, "America/Manaus"                },
	{ "amst",  1, -10800, "America/Porto_Velho"           },
	{ "amst",  1, -10800, "America/Santarem"              },
	{ "amst",  1, -10800, "Brazil/West"                   },
	{ "amt",   0,  14400, "Asia/Yerevan"                  },
	{ "amt",   0,  10800, "Asia/Yerevan"                  },
	{ "amt",   0, -13840, "America/Asuncion"              },
	{ "amt",   0, -14400, "America/Boa_Vista"             },
	{ "amt",   0, -14400, "America/Campo_Grande"          },
	{ "amt",   0, -14400, "America/Cuiaba"                },
	{ "amt",   0, -14400, "America/Eirunepe"              },
	{ "amt",   0, -14400, "America/Manaus"                },
	{ "amt",   0, -14400, "America/Porto_Acre"            },
	{ "amt",   0, -14400, "America/Porto_Velho"           },
	{ "amt",   0, -14400, "America/Rio_Branco"            },
	{ "amt",   0, -14400, "America/Santarem"              },
	{ "amt",   0, -14400, "Brazil/Acre"                   },
	{ "amt",   0, -14400, "Brazil/West"                   },
	{ "amt",   0,   1172, "Europe/Amsterdam"              },
	{ "amt",   0,   5692, "Europe/Athens"                 },
	{ "anast", 1,  50400, "Asia/Anadyr"                   },
	{ "anast", 1,  43200, "Asia/Anadyr"                   },
	{ "anast", 1,  46800, "Asia/Anadyr"                   },
	{ "anat",  0,  46800, "Asia/Anadyr"                   },
	{ "anat",  0,  39600, "Asia/Anadyr"                   },
	{ "anat",  0,  43200, "Asia/Anadyr"                   },
	{ "ant",   0, -16200, "America/Curacao"               },
	{ "ant",   0, -16200, "America/Aruba"                 },
	{ "ant",   0, -16200, "America/Kralendijk"            },
	{ "ant",   0, -16200, "America/Lower_Princes"         },
	{ "aot",   0,   3124, "Africa/Luanda"                 },
	{ "apt",   1, -10800, "America/Halifax"               },
	{ "apt",   1, -10800, "America/Blanc-Sablon"          },
	{ "apt",   1, -10800, "America/Glace_Bay"             },
	{ "apt",   1, -10800, "America/Moncton"               },
	{ "apt",   1, -10800, "America/Pangnirtung"           },
	{ "apt",   1, -10800, "America/Puerto_Rico"           },
	{ "apt",   1, -10800, "Canada/Atlantic"               },
	{ "aqtst", 1,  21600, "Asia/Aqtau"                    },
	{ "aqtst", 1,  18000, "Asia/Aqtau"                    },
	{ "aqtst", 1,  21600, "Asia/Aqtobe"                   },
	{ "aqtt",  0,  18000, "Asia/Aqtau"                    },
	{ "aqtt",  0,  14400, "Asia/Aqtau"                    },
	{ "aqtt",  0,  18000, "Asia/Aqtobe"                   },
	{ "arst",  1,  -7200, "America/Buenos_Aires"          },
	{ "arst",  1, -10800, "America/Buenos_Aires"          },
	{ "arst",  1, -10800, "America/Argentina/Buenos_Aires" },
	{ "arst",  1, -10800, "America/Argentina/Catamarca"   },
	{ "arst",  1, -10800, "America/Argentina/ComodRivadavia" },
	{ "arst",  1, -10800, "America/Argentina/Cordoba"     },
	{ "arst",  1, -10800, "America/Argentina/Jujuy"       },
	{ "arst",  1, -10800, "America/Argentina/La_Rioja"    },
	{ "arst",  1, -10800, "America/Argentina/Mendoza"     },
	{ "arst",  1, -10800, "America/Argentina/Rio_Gallegos" },
	{ "arst",  1, -10800, "America/Argentina/Salta"       },
	{ "arst",  1, -10800, "America/Argentina/San_Juan"    },
	{ "arst",  1, -10800, "America/Argentina/San_Luis"    },
	{ "arst",  1, -10800, "America/Argentina/Tucuman"     },
	{ "arst",  1, -10800, "America/Argentina/Ushuaia"     },
	{ "arst",  1, -10800, "America/Catamarca"             },
	{ "arst",  1, -10800, "America/Cordoba"               },
	{ "arst",  1, -10800, "America/Jujuy"                 },
	{ "arst",  1, -10800, "America/Mendoza"               },
	{ "arst",  1, -10800, "America/Rosario"               },
	{ "arst",  1, -10800, "Antarctica/Palmer"             },
	{ "arst",  1,  -7200, "America/Argentina/Buenos_Aires" },
	{ "arst",  1,  -7200, "America/Argentina/Catamarca"   },
	{ "arst",  1,  -7200, "America/Argentina/ComodRivadavia" },
	{ "arst",  1,  -7200, "America/Argentina/Cordoba"     },
	{ "arst",  1,  -7200, "America/Argentina/Jujuy"       },
	{ "arst",  1,  -7200, "America/Argentina/La_Rioja"    },
	{ "arst",  1,  -7200, "America/Argentina/Mendoza"     },
	{ "arst",  1,  -7200, "America/Argentina/Rio_Gallegos" },
	{ "arst",  1,  -7200, "America/Argentina/Salta"       },
	{ "arst",  1,  -7200, "America/Argentina/San_Juan"    },
	{ "arst",  1,  -7200, "America/Argentina/San_Luis"    },
	{ "arst",  1,  -7200, "America/Argentina/Tucuman"     },
	{ "arst",  1,  -7200, "America/Argentina/Ushuaia"     },
	{ "arst",  1,  -7200, "America/Catamarca"             },
	{ "arst",  1,  -7200, "America/Cordoba"               },
	{ "arst",  1,  -7200, "America/Jujuy"                 },
	{ "arst",  1,  -7200, "America/Mendoza"               },
	{ "arst",  1,  -7200, "America/Rosario"               },
	{ "arst",  1,  -7200, "Antarctica/Palmer"             },
	{ "art",   0, -10800, "America/Buenos_Aires"          },
	{ "art",   0, -14400, "America/Buenos_Aires"          },
	{ "art",   0, -10800, "America/Argentina/Buenos_Aires" },
	{ "art",   0, -10800, "America/Argentina/Catamarca"   },
	{ "art",   0, -10800, "America/Argentina/ComodRivadavia" },
	{ "art",   0, -10800, "America/Argentina/Cordoba"     },
	{ "art",   0, -10800, "America/Argentina/Jujuy"       },
	{ "art",   0, -10800, "America/Argentina/La_Rioja"    },
	{ "art",   0, -10800, "America/Argentina/Mendoza"     },
	{ "art",   0, -10800, "America/Argentina/Rio_Gallegos" },
	{ "art",   0, -10800, "America/Argentina/Salta"       },
	{ "art",   0, -10800, "America/Argentina/San_Juan"    },
	{ "art",   0, -10800, "America/Argentina/San_Luis"    },
	{ "art",   0, -10800, "America/Argentina/Tucuman"     },
	{ "art",   0, -10800, "America/Argentina/Ushuaia"     },
	{ "art",   0, -10800, "America/Catamarca"             },
	{ "art",   0, -10800, "America/Cordoba"               },
	{ "art",   0, -10800, "America/Jujuy"                 },
	{ "art",   0, -10800, "America/Mendoza"               },
	{ "art",   0, -10800, "America/Rosario"               },
	{ "art",   0, -10800, "Antarctica/Palmer"             },
	{ "art",   0, -14400, "America/Argentina/Buenos_Aires" },
	{ "art",   0, -14400, "America/Argentina/Catamarca"   },
	{ "art",   0, -14400, "America/Argentina/ComodRivadavia" },
	{ "art",   0, -14400, "America/Argentina/Cordoba"     },
	{ "art",   0, -14400, "America/Argentina/Jujuy"       },
	{ "art",   0, -14400, "America/Argentina/La_Rioja"    },
	{ "art",   0, -14400, "America/Argentina/Mendoza"     },
	{ "art",   0, -14400, "America/Argentina/Rio_Gallegos" },
	{ "art",   0, -14400, "America/Argentina/Salta"       },
	{ "art",   0, -14400, "America/Argentina/San_Juan"    },
	{ "art",   0, -14400, "America/Argentina/San_Luis"    },
	{ "art",   0, -14400, "America/Argentina/Tucuman"     },
	{ "art",   0, -14400, "America/Argentina/Ushuaia"     },
	{ "art",   0, -14400, "America/Catamarca"             },
	{ "art",   0, -14400, "America/Cordoba"               },
	{ "art",   0, -14400, "America/Jujuy"                 },
	{ "art",   0, -14400, "America/Mendoza"               },
	{ "art",   0, -14400, "America/Rosario"               },
	{ "art",   0, -14400, "Antarctica/Palmer"             },
	{ "ashst", 1,  21600, "Asia/Ashkhabad"                },
	{ "ashst", 1,  18000, "Asia/Ashkhabad"                },
	{ "ashst", 1,  18000, "Asia/Ashgabat"                 },
	{ "ashst", 1,  21600, "Asia/Ashgabat"                 },
	{ "asht",  0,  18000, "Asia/Ashkhabad"                },
	{ "asht",  0,  14400, "Asia/Ashkhabad"                },
	{ "asht",  0,  14400, "Asia/Ashgabat"                 },
	{ "asht",  0,  18000, "Asia/Ashgabat"                 },
	{ "ast",   0,  10800, "Asia/Riyadh"                   },
	{ "ast",   0, -14400, "America/Anguilla"              },
	{ "ast",   0, -14400, "America/Antigua"               },
	{ "ast",   0, -14400, "America/Aruba"                 },
	{ "ast",   0, -14400, "America/Barbados"              },
	{ "ast",   0, -14400, "America/Blanc-Sablon"          },
	{ "ast",   0, -14400, "America/Curacao"               },
	{ "ast",   0, -14400, "America/Dominica"              },
	{ "ast",   0, -14400, "America/Glace_Bay"             },
	{ "ast",   0, -14400, "America/Goose_Bay"             },
	{ "ast",   0, -14400, "America/Grenada"               },
	{ "ast",   0, -14400, "America/Guadeloupe"            },
	{ "ast",   0, -14400, "America/Halifax"               },
	{ "ast",   0, -14400, "America/Kralendijk"            },
	{ "ast",   0, -14400, "America/Lower_Princes"         },
	{ "ast",   0, -14400, "America/Marigot"               },
	{ "ast",   0, -14400, "America/Martinique"            },
	{ "ast",   0, -14400, "America/Miquelon"              },
	{ "ast",   0, -14400, "America/Moncton"               },
	{ "ast",   0, -14400, "America/Montserrat"            },
	{ "ast",   0, -14400, "America/Pangnirtung"           },
	{ "ast",   0, -14400, "America/Port_of_Spain"         },
	{ "ast",   0, -14400, "America/Puerto_Rico"           },
	{ "ast",   0, -14400, "America/Santo_Domingo"         },
	{ "ast",   0, -14400, "America/St_Barthelemy"         },
	{ "ast",   0, -14400, "America/St_Kitts"              },
	{ "ast",   0, -14400, "America/St_Lucia"              },
	{ "ast",   0, -14400, "America/St_Thomas"             },
	{ "ast",   0, -14400, "America/St_Vincent"            },
	{ "ast",   0, -14400, "America/Thule"                 },
	{ "ast",   0, -14400, "America/Tortola"               },
	{ "ast",   0, -14400, "America/Virgin"                },
	{ "ast",   0, -14400, "Atlantic/Bermuda"              },
	{ "ast",   0, -14400, "Canada/Atlantic"               },
	{ "ast",   0,  10800, "Asia/Aden"                     },
	{ "ast",   0,  10800, "Asia/Baghdad"                  },
	{ "ast",   0,  10800, "Asia/Bahrain"                  },
	{ "ast",   0,  10800, "Asia/Kuwait"                   },
	{ "ast",   0,  10800, "Asia/Qatar"                    },
	{ "awdt",  1,  32400, "Australia/Perth"               },
	{ "awdt",  1,  32400, "Australia/West"                },
	{ "awst",  0,  28800, "Australia/Perth"               },
	{ "awst",  0,  28800, "Antarctica/Casey"              },
	{ "awst",  0,  28800, "Australia/West"                },
	{ "awt",   1, -10800, "America/Halifax"               },
	{ "awt",   1, -10800, "America/Blanc-Sablon"          },
	{ "awt",   1, -10800, "America/Glace_Bay"             },
	{ "awt",   1, -10800, "America/Moncton"               },
	{ "awt",   1, -10800, "America/Pangnirtung"           },
	{ "awt",   1, -10800, "America/Puerto_Rico"           },
	{ "awt",   1, -10800, "Canada/Atlantic"               },
	{ "azomt", 1,      0, "Atlantic/Azores"               },
	{ "azost", 1,      0, "Atlantic/Azores"               },
	{ "azost", 1,  -3600, "Atlantic/Azores"               },
	{ "azot",  0,  -3600, "Atlantic/Azores"               },
	{ "azot",  0,  -7200, "Atlantic/Azores"               },
	{ "azst",  1,  18000, "Asia/Baku"                     },
	{ "azst",  1,  14400, "Asia/Baku"                     },
	{ "azt",   0,  14400, "Asia/Baku"                     },
	{ "azt",   0,  10800, "Asia/Baku"                     },
	{ "bakst", 1,  18000, "Asia/Baku"                     },
	{ "bakst", 1,  14400, "Asia/Baku"                     },
	{ "bakt",  0,  14400, "Asia/Baku"                     },
	{ "bakt",  0,  10800, "Asia/Baku"                     },
	{ "bdst",  1,   7200, "Europe/London"                 },
	{ "bdst",  1,  25200, "Asia/Dacca"                    },
	{ "bdst",  1,  25200, "Asia/Dhaka"                    },
	{ "bdst",  1,   7200, "Europe/Belfast"                },
	{ "bdst",  1,   7200, "Europe/Gibraltar"              },
	{ "bdst",  1,   7200, "Europe/Guernsey"               },
	{ "bdst",  1,   7200, "Europe/Isle_of_Man"            },
	{ "bdst",  1,   7200, "Europe/Jersey"                 },
	{ "bdst",  1,   7200, "GB"                            },
	{ "bdt",   1, -36000, "America/Adak"                  },
	{ "bdt",   0,  21600, "Asia/Dacca"                    },
	{ "bdt",   1, -36000, "America/Atka"                  },
	{ "bdt",   1, -36000, "America/Nome"                  },
	{ "bdt",   0,  21600, "Asia/Dhaka"                    },
	{ "beat",  0,   9000, "Africa/Mogadishu"              },
	{ "beat",  0,   9000, "Africa/Kampala"                },
	{ "beat",  0,   9000, "Africa/Nairobi"                },
	{ "beaut", 0,   9900, "Africa/Nairobi"                },
	{ "beaut", 0,   9900, "Africa/Dar_es_Salaam"          },
	{ "beaut", 0,   9900, "Africa/Kampala"                },
	{ "bmt",   0, -14309, "America/Barbados"              },
	{ "bmt",   0,   6264, "Europe/Tiraspol"               },
	{ "bmt",   0, -17776, "America/Bogota"                },
	{ "bmt",   0,  10656, "Asia/Baghdad"                  },
	{ "bmt",   0,  24124, "Asia/Bangkok"                  },
	{ "bmt",   0,  25632, "Asia/Jakarta"                  },
	{ "bmt",   0,   6264, "Europe/Bucharest"              },
	{ "bmt",   0,   6264, "Europe/Chisinau"               },
	{ "bnt",   0,  28800, "Asia/Brunei"                   },
	{ "bnt",   0,  27000, "Asia/Brunei"                   },
	{ "bortst", 1,  30000, "Asia/Kuching"                  },
	{ "bort",  0,  28800, "Asia/Kuching"                  },
	{ "bort",  0,  27000, "Asia/Kuching"                  },
	{ "bost",  1, -12756, "America/La_Paz"                },
	{ "bot",   0, -14400, "America/La_Paz"                },
	{ "brst",  1,  -7200, "America/Sao_Paulo"             },
	{ "brst",  1,  -7200, "America/Araguaina"             },
	{ "brst",  1,  -7200, "America/Bahia"                 },
	{ "brst",  1,  -7200, "America/Belem"                 },
	{ "brst",  1,  -7200, "America/Fortaleza"             },
	{ "brst",  1,  -7200, "America/Maceio"                },
	{ "brst",  1,  -7200, "America/Recife"                },
	{ "brst",  1,  -7200, "Brazil/East"                   },
	{ "brt",   0, -10800, "America/Sao_Paulo"             },
	{ "brt",   0, -10800, "America/Araguaina"             },
	{ "brt",   0, -10800, "America/Bahia"                 },
	{ "brt",   0, -10800, "America/Belem"                 },
	{ "brt",   0, -10800, "America/Fortaleza"             },
	{ "brt",   0, -10800, "America/Maceio"                },
	{ "brt",   0, -10800, "America/Recife"                },
	{ "brt",   0, -10800, "America/Santarem"              },
	{ "brt",   0, -10800, "Brazil/East"                   },
	{ "bst",   1,   3600, "Europe/London"                 },
	{ "bst",   0,   3600, "Europe/London"                 },
	{ "bst",   0, -39600, "America/Adak"                  },
	{ "bst",   0, -39600, "America/Atka"                  },
	{ "bst",   0, -39600, "America/Nome"                  },
	{ "bst",   0, -39600, "Pacific/Midway"                },
	{ "bst",   0, -39600, "Pacific/Pago_Pago"             },
	{ "bst",   0, -39600, "Pacific/Samoa"                 },
	{ "bst",   0,   3600, "Europe/Belfast"                },
	{ "bst",   0,   3600, "Europe/Guernsey"               },
	{ "bst",   0,   3600, "Europe/Isle_of_Man"            },
	{ "bst",   0,   3600, "Europe/Jersey"                 },
	{ "bst",   0,   3600, "GB"                            },
	{ "bst",   1,   3600, "Europe/Belfast"                },
	{ "bst",   1,   3600, "Europe/Dublin"                 },
	{ "bst",   1,   3600, "Europe/Gibraltar"              },
	{ "bst",   1,   3600, "Europe/Guernsey"               },
	{ "bst",   1,   3600, "Europe/Isle_of_Man"            },
	{ "bst",   1,   3600, "Europe/Jersey"                 },
	{ "bst",   1,   3600, "GB"                            },
	{ "btt",   0,  21600, "Asia/Thimbu"                   },
	{ "btt",   0,  21600, "Asia/Thimphu"                  },
	{ "burt",  0,  23400, "Asia/Kolkata"                  },
	{ "burt",  0,  23400, "Asia/Calcutta"                 },
	{ "burt",  0,  23400, "Asia/Dacca"                    },
	{ "burt",  0,  23400, "Asia/Dhaka"                    },
	{ "burt",  0,  23400, "Asia/Rangoon"                  },
	{ "cant",  0,  -3600, "Atlantic/Canary"               },
	{ "capt",  1, -32400, "America/Anchorage"             },
	{ "cast",  0,  34200, "Australia/Adelaide"            },
	{ "cast",  1,  10800, "Africa/Gaborone"               },
	{ "cast",  1,  10800, "Africa/Juba"                   },
	{ "cast",  1,  10800, "Africa/Khartoum"               },
	{ "cast",  0,  39600, "Antarctica/Casey"              },
	{ "cat",   0, -36000, "America/Anchorage"             },
	{ "cat",   0,   7200, "Africa/Khartoum"               },
	{ "cat",   0,   7200, "Africa/Blantyre"               },
	{ "cat",   0,   7200, "Africa/Bujumbura"              },
	{ "cat",   0,   7200, "Africa/Gaborone"               },
	{ "cat",   0,   7200, "Africa/Harare"                 },
	{ "cat",   0,   7200, "Africa/Juba"                   },
	{ "cat",   0,   7200, "Africa/Kigali"                 },
	{ "cat",   0,   7200, "Africa/Lubumbashi"             },
	{ "cat",   0,   7200, "Africa/Lusaka"                 },
	{ "cat",   0,   7200, "Africa/Maputo"                 },
	{ "cat",   0,   7200, "Africa/Windhoek"               },
	{ "cawt",  1, -32400, "America/Anchorage"             },
	{ "cct",   0,  23400, "Indian/Cocos"                  },
	{ "cddt",  1, -14400, "America/Rankin_Inlet"          },
	{ "cddt",  1, -14400, "America/Resolute"              },
	{ "cdt",   1, -18000, "America/Chicago"               },
	{ "cdt",   1,  32400, "Asia/Shanghai"                 },
	{ "cdt",   1, -14400, "America/Havana"                },
	{ "cdt",   1, -18000, "America/Atikokan"              },
	{ "cdt",   1, -18000, "America/Bahia_Banderas"        },
	{ "cdt",   1, -18000, "America/Belize"                },
	{ "cdt",   1, -18000, "America/Cambridge_Bay"         },
	{ "cdt",   1, -18000, "America/Cancun"                },
	{ "cdt",   1, -18000, "America/Chihuahua"             },
	{ "cdt",   1, -18000, "America/Coral_Harbour"         },
	{ "cdt",   1, -18000, "America/Costa_Rica"            },
	{ "cdt",   1, -18000, "America/El_Salvador"           },
	{ "cdt",   1, -18000, "America/Fort_Wayne"            },
	{ "cdt",   1, -18000, "America/Guatemala"             },
	{ "cdt",   1, -18000, "America/Indiana/Indianapolis"  },
	{ "cdt",   1, -18000, "America/Indiana/Knox"          },
	{ "cdt",   1, -18000, "America/Indiana/Marengo"       },
	{ "cdt",   1, -18000, "America/Indiana/Petersburg"    },
	{ "cdt",   1, -18000, "America/Indiana/Tell_City"     },
	{ "cdt",   1, -18000, "America/Indiana/Vevay"         },
	{ "cdt",   1, -18000, "America/Indiana/Vincennes"     },
	{ "cdt",   1, -18000, "America/Indiana/Winamac"       },
	{ "cdt",   1, -18000, "America/Indianapolis"          },
	{ "cdt",   1, -18000, "America/Iqaluit"               },
	{ "cdt",   1, -18000, "America/Kentucky/Louisville"   },
	{ "cdt",   1, -18000, "America/Kentucky/Monticello"   },
	{ "cdt",   1, -18000, "America/Knox_IN"               },
	{ "cdt",   1, -18000, "America/Louisville"            },
	{ "cdt",   1, -18000, "America/Managua"               },
	{ "cdt",   1, -18000, "America/Matamoros"             },
	{ "cdt",   1, -18000, "America/Menominee"             },
	{ "cdt",   1, -18000, "America/Merida"                },
	{ "cdt",   1, -18000, "America/Mexico_City"           },
	{ "cdt",   1, -18000, "America/Monterrey"             },
	{ "cdt",   1, -18000, "America/North_Dakota/Beulah"   },
	{ "cdt",   1, -18000, "America/North_Dakota/Center"   },
	{ "cdt",   1, -18000, "America/North_Dakota/New_Salem" },
	{ "cdt",   1, -18000, "America/Ojinaga"               },
	{ "cdt",   1, -18000, "America/Pangnirtung"           },
	{ "cdt",   1, -18000, "America/Rainy_River"           },
	{ "cdt",   1, -18000, "America/Rankin_Inlet"          },
	{ "cdt",   1, -18000, "America/Resolute"              },
	{ "cdt",   1, -18000, "America/Tegucigalpa"           },
	{ "cdt",   1, -18000, "America/Winnipeg"              },
	{ "cdt",   1, -18000, "Canada/Central"                },
	{ "cdt",   1, -18000, "Mexico/General"                },
	{ "cdt",   1,  32400, "Asia/Chongqing"                },
	{ "cdt",   1,  32400, "Asia/Chungking"                },
	{ "cdt",   1,  32400, "Asia/Harbin"                   },
	{ "cdt",   1,  32400, "Asia/Taipei"                   },
	{ "cdt",   1,  32400, "PRC"                           },
	{ "cdt",   1,  32400, "ROC"                           },
	{ "cemt",  1,  10800, "Europe/Berlin"                 },
	{ "cemt",  1,  10800, "CET"                           },
	{ "cest",  1,   7200, "Europe/Berlin"                 },
	{ "cest",  1,  10800, "Europe/Kaliningrad"            },
	{ "cest",  1,   7200, "Africa/Algiers"                },
	{ "cest",  1,   7200, "Africa/Ceuta"                  },
	{ "cest",  1,   7200, "Africa/Tripoli"                },
	{ "cest",  1,   7200, "Africa/Tunis"                  },
	{ "cest",  1,   7200, "Antarctica/Troll"              },
	{ "cest",  1,   7200, "Arctic/Longyearbyen"           },
	{ "cest",  1,   7200, "Atlantic/Jan_Mayen"            },
	{ "cest",  1,   7200, "CET"                           },
	{ "cest",  1,   7200, "Europe/Amsterdam"              },
	{ "cest",  1,   7200, "Europe/Andorra"                },
	{ "cest",  1,   7200, "Europe/Athens"                 },
	{ "cest",  1,   7200, "Europe/Belgrade"               },
	{ "cest",  1,   7200, "Europe/Bratislava"             },
	{ "cest",  1,   7200, "Europe/Brussels"               },
	{ "cest",  1,   7200, "Europe/Budapest"               },
	{ "cest",  1,   7200, "Europe/Busingen"               },
	{ "cest",  1,   7200, "Europe/Chisinau"               },
	{ "cest",  1,   7200, "Europe/Copenhagen"             },
	{ "cest",  1,   7200, "Europe/Gibraltar"              },
	{ "cest",  1,   7200, "Europe/Kaliningrad"            },
	{ "cest",  1,   7200, "Europe/Kiev"                   },
	{ "cest",  1,   7200, "Europe/Lisbon"                 },
	{ "cest",  1,   7200, "Europe/Ljubljana"              },
	{ "cest",  1,   7200, "Europe/Luxembourg"             },
	{ "cest",  1,   7200, "Europe/Madrid"                 },
	{ "cest",  1,   7200, "Europe/Malta"                  },
	{ "cest",  1,   7200, "Europe/Minsk"                  },
	{ "cest",  1,   7200, "Europe/Monaco"                 },
	{ "cest",  1,   7200, "Europe/Oslo"                   },
	{ "cest",  1,   7200, "Europe/Paris"                  },
	{ "cest",  1,   7200, "Europe/Podgorica"              },
	{ "cest",  1,   7200, "Europe/Prague"                 },
	{ "cest",  1,   7200, "Europe/Riga"                   },
	{ "cest",  1,   7200, "Europe/Rome"                   },
	{ "cest",  1,   7200, "Europe/San_Marino"             },
	{ "cest",  1,   7200, "Europe/Sarajevo"               },
	{ "cest",  1,   7200, "Europe/Simferopol"             },
	{ "cest",  1,   7200, "Europe/Skopje"                 },
	{ "cest",  1,   7200, "Europe/Sofia"                  },
	{ "cest",  1,   7200, "Europe/Stockholm"              },
	{ "cest",  1,   7200, "Europe/Tallinn"                },
	{ "cest",  1,   7200, "Europe/Tirane"                 },
	{ "cest",  1,   7200, "Europe/Tiraspol"               },
	{ "cest",  1,   7200, "Europe/Uzhgorod"               },
	{ "cest",  1,   7200, "Europe/Vaduz"                  },
	{ "cest",  1,   7200, "Europe/Vatican"                },
	{ "cest",  1,   7200, "Europe/Vienna"                 },
	{ "cest",  1,   7200, "Europe/Vilnius"                },
	{ "cest",  1,   7200, "Europe/Warsaw"                 },
	{ "cest",  1,   7200, "Europe/Zagreb"                 },
	{ "cest",  1,   7200, "Europe/Zaporozhye"             },
	{ "cest",  1,   7200, "Europe/Zurich"                 },
	{ "cest",  1,   7200, "WET"                           },
	{ "cet",   0,   3600, "Europe/Berlin"                 },
	{ "cet",   0,   7200, "Europe/Kaliningrad"            },
	{ "cet",   0,   3600, "Africa/Algiers"                },
	{ "cet",   0,   3600, "Africa/Casablanca"             },
	{ "cet",   0,   3600, "Africa/Ceuta"                  },
	{ "cet",   0,   3600, "Africa/Tripoli"                },
	{ "cet",   0,   3600, "Africa/Tunis"                  },
	{ "cet",   0,   3600, "Arctic/Longyearbyen"           },
	{ "cet",   0,   3600, "Atlantic/Jan_Mayen"            },
	{ "cet",   0,   3600, "CET"                           },
	{ "cet",   0,   3600, "Europe/Amsterdam"              },
	{ "cet",   0,   3600, "Europe/Andorra"                },
	{ "cet",   0,   3600, "Europe/Athens"                 },
	{ "cet",   0,   3600, "Europe/Belgrade"               },
	{ "cet",   0,   3600, "Europe/Bratislava"             },
	{ "cet",   0,   3600, "Europe/Brussels"               },
	{ "cet",   0,   3600, "Europe/Budapest"               },
	{ "cet",   0,   3600, "Europe/Busingen"               },
	{ "cet",   0,   3600, "Europe/Chisinau"               },
	{ "cet",   0,   3600, "Europe/Copenhagen"             },
	{ "cet",   0,   3600, "Europe/Gibraltar"              },
	{ "cet",   0,   3600, "Europe/Kaliningrad"            },
	{ "cet",   0,   3600, "Europe/Kiev"                   },
	{ "cet",   0,   3600, "Europe/Lisbon"                 },
	{ "cet",   0,   3600, "Europe/Ljubljana"              },
	{ "cet",   0,   3600, "Europe/Luxembourg"             },
	{ "cet",   0,   3600, "Europe/Madrid"                 },
	{ "cet",   0,   3600, "Europe/Malta"                  },
	{ "cet",   0,   3600, "Europe/Minsk"                  },
	{ "cet",   0,   3600, "Europe/Monaco"                 },
	{ "cet",   0,   3600, "Europe/Oslo"                   },
	{ "cet",   0,   3600, "Europe/Paris"                  },
	{ "cet",   0,   3600, "Europe/Podgorica"              },
	{ "cet",   0,   3600, "Europe/Prague"                 },
	{ "cet",   0,   3600, "Europe/Riga"                   },
	{ "cet",   0,   3600, "Europe/Rome"                   },
	{ "cet",   0,   3600, "Europe/San_Marino"             },
	{ "cet",   0,   3600, "Europe/Sarajevo"               },
	{ "cet",   0,   3600, "Europe/Simferopol"             },
	{ "cet",   0,   3600, "Europe/Skopje"                 },
	{ "cet",   0,   3600, "Europe/Sofia"                  },
	{ "cet",   0,   3600, "Europe/Stockholm"              },
	{ "cet",   0,   3600, "Europe/Tallinn"                },
	{ "cet",   0,   3600, "Europe/Tirane"                 },
	{ "cet",   0,   3600, "Europe/Tiraspol"               },
	{ "cet",   0,   3600, "Europe/Uzhgorod"               },
	{ "cet",   0,   3600, "Europe/Vaduz"                  },
	{ "cet",   0,   3600, "Europe/Vatican"                },
	{ "cet",   0,   3600, "Europe/Vienna"                 },
	{ "cet",   0,   3600, "Europe/Vilnius"                },
	{ "cet",   0,   3600, "Europe/Warsaw"                 },
	{ "cet",   0,   3600, "Europe/Zagreb"                 },
	{ "cet",   0,   3600, "Europe/Zaporozhye"             },
	{ "cet",   0,   3600, "Europe/Zurich"                 },
	{ "cet",   0,   3600, "WET"                           },
	{ "cgst",  1,  -3600, "America/Scoresbysund"          },
	{ "cgt",   0,  -7200, "America/Scoresbysund"          },
	{ "chadt", 1,  49500, "Pacific/Chatham"               },
	{ "chast", 0,  45900, "Pacific/Chatham"               },
	{ "chast", 0,  44100, "Pacific/Chatham"               },
	{ "chdt",  1, -19800, "America/Belize"                },
	{ "chost", 1,  36000, "Asia/Choibalsan"               },
	{ "chot",  0,  32400, "Asia/Choibalsan"               },
	{ "chot",  0,  28800, "Asia/Choibalsan"               },
	{ "chut",  0,  36000, "Pacific/Chuuk"                 },
	{ "chut",  0,  36000, "Pacific/Truk"                  },
	{ "chut",  0,  36000, "Pacific/Yap"                   },
	{ "ckhst", 1, -34200, "Pacific/Rarotonga"             },
	{ "ckt",   0, -36000, "Pacific/Rarotonga"             },
	{ "ckt",   0, -37800, "Pacific/Rarotonga"             },
	{ "clst",  1, -10800, "America/Santiago"              },
	{ "clst",  1, -14400, "America/Santiago"              },
	{ "clst",  1, -10800, "Antarctica/Palmer"             },
	{ "clst",  1, -10800, "Chile/Continental"             },
	{ "clst",  1, -14400, "Chile/Continental"             },
	{ "clt",   0, -14400, "America/Santiago"              },
	{ "clt",   0, -18000, "America/Santiago"              },
	{ "clt",   0, -14400, "Antarctica/Palmer"             },
	{ "clt",   0, -14400, "Chile/Continental"             },
	{ "clt",   0, -18000, "Chile/Continental"             },
	{ "cmt",   0, -15408, "America/Argentina/Buenos_Aires" },
	{ "cmt",   0, -15408, "America/Argentina/Catamarca"   },
	{ "cmt",   0, -15408, "America/Argentina/ComodRivadavia" },
	{ "cmt",   0, -15408, "America/Argentina/Cordoba"     },
	{ "cmt",   0, -15408, "America/Argentina/Jujuy"       },
	{ "cmt",   0, -15408, "America/Argentina/La_Rioja"    },
	{ "cmt",   0, -15408, "America/Argentina/Mendoza"     },
	{ "cmt",   0, -15408, "America/Argentina/Rio_Gallegos" },
	{ "cmt",   0, -15408, "America/Argentina/Salta"       },
	{ "cmt",   0, -15408, "America/Argentina/San_Juan"    },
	{ "cmt",   0, -15408, "America/Argentina/San_Luis"    },
	{ "cmt",   0, -15408, "America/Argentina/Tucuman"     },
	{ "cmt",   0, -15408, "America/Argentina/Ushuaia"     },
	{ "cmt",   0, -15408, "America/Buenos_Aires"          },
	{ "cmt",   0, -15408, "America/Catamarca"             },
	{ "cmt",   0, -15408, "America/Cordoba"               },
	{ "cmt",   0, -15408, "America/Jujuy"                 },
	{ "cmt",   0, -15408, "America/Mendoza"               },
	{ "cmt",   0, -15408, "America/Rosario"               },
	{ "cmt",   0, -16060, "America/Caracas"               },
	{ "cmt",   0, -16356, "America/La_Paz"                },
	{ "cmt",   0, -19176, "America/Panama"                },
	{ "cmt",   0,   6900, "Europe/Chisinau"               },
	{ "cmt",   0,   6900, "Europe/Tiraspol"               },
	{ "cost",  1, -14400, "America/Bogota"                },
	{ "cot",   0, -18000, "America/Bogota"                },
	{ "cpt",   1, -18000, "America/Chicago"               },
	{ "cpt",   1, -18000, "America/Atikokan"              },
	{ "cpt",   1, -18000, "America/Coral_Harbour"         },
	{ "cpt",   1, -18000, "America/Fort_Wayne"            },
	{ "cpt",   1, -18000, "America/Indiana/Indianapolis"  },
	{ "cpt",   1, -18000, "America/Indiana/Knox"          },
	{ "cpt",   1, -18000, "America/Indiana/Marengo"       },
	{ "cpt",   1, -18000, "America/Indiana/Petersburg"    },
	{ "cpt",   1, -18000, "America/Indiana/Tell_City"     },
	{ "cpt",   1, -18000, "America/Indiana/Vevay"         },
	{ "cpt",   1, -18000, "America/Indiana/Vincennes"     },
	{ "cpt",   1, -18000, "America/Indiana/Winamac"       },
	{ "cpt",   1, -18000, "America/Indianapolis"          },
	{ "cpt",   1, -18000, "America/Kentucky/Louisville"   },
	{ "cpt",   1, -18000, "America/Kentucky/Monticello"   },
	{ "cpt",   1, -18000, "America/Knox_IN"               },
	{ "cpt",   1, -18000, "America/Louisville"            },
	{ "cpt",   1, -18000, "America/Menominee"             },
	{ "cpt",   1, -18000, "America/Rainy_River"           },
	{ "cpt",   1, -18000, "America/Winnipeg"              },
	{ "cpt",   1, -18000, "Canada/Central"                },
	{ "cst",   0, -21600, "America/Chicago"               },
	{ "cst",   0, -18000, "America/Havana"                },
	{ "cst",   0, -21600, "America/Atikokan"              },
	{ "cst",   0, -21600, "America/Bahia_Banderas"        },
	{ "cst",   0, -21600, "America/Belize"                },
	{ "cst",   0, -21600, "America/Cambridge_Bay"         },
	{ "cst",   0, -21600, "America/Cancun"                },
	{ "cst",   0, -21600, "America/Chihuahua"             },
	{ "cst",   0, -21600, "America/Coral_Harbour"         },
	{ "cst",   0, -21600, "America/Costa_Rica"            },
	{ "cst",   0, -21600, "America/Detroit"               },
	{ "cst",   0, -21600, "America/El_Salvador"           },
	{ "cst",   0, -21600, "America/Fort_Wayne"            },
	{ "cst",   0, -21600, "America/Guatemala"             },
	{ "cst",   0, -21600, "America/Hermosillo"            },
	{ "cst",   0, -21600, "America/Indiana/Indianapolis"  },
	{ "cst",   0, -21600, "America/Indiana/Knox"          },
	{ "cst",   0, -21600, "America/Indiana/Marengo"       },
	{ "cst",   0, -21600, "America/Indiana/Petersburg"    },
	{ "cst",   0, -21600, "America/Indiana/Tell_City"     },
	{ "cst",   0, -21600, "America/Indiana/Vevay"         },
	{ "cst",   0, -21600, "America/Indiana/Vincennes"     },
	{ "cst",   0, -21600, "America/Indiana/Winamac"       },
	{ "cst",   0, -21600, "America/Indianapolis"          },
	{ "cst",   0, -21600, "America/Iqaluit"               },
	{ "cst",   0, -21600, "America/Kentucky/Louisville"   },
	{ "cst",   0, -21600, "America/Kentucky/Monticello"   },
	{ "cst",   0, -21600, "America/Knox_IN"               },
	{ "cst",   0, -21600, "America/Louisville"            },
	{ "cst",   0, -21600, "America/Managua"               },
	{ "cst",   0, -21600, "America/Matamoros"             },
	{ "cst",   0, -21600, "America/Mazatlan"              },
	{ "cst",   0, -21600, "America/Menominee"             },
	{ "cst",   0, -21600, "America/Merida"                },
	{ "cst",   0, -21600, "America/Mexico_City"           },
	{ "cst",   0, -21600, "America/Monterrey"             },
	{ "cst",   0, -21600, "America/North_Dakota/Beulah"   },
	{ "cst",   0, -21600, "America/North_Dakota/Center"   },
	{ "cst",   0, -21600, "America/North_Dakota/New_Salem" },
	{ "cst",   0, -21600, "America/Ojinaga"               },
	{ "cst",   0, -21600, "America/Pangnirtung"           },
	{ "cst",   0, -21600, "America/Rainy_River"           },
	{ "cst",   0, -21600, "America/Rankin_Inlet"          },
	{ "cst",   0, -21600, "America/Regina"                },
	{ "cst",   0, -21600, "America/Resolute"              },
	{ "cst",   0, -21600, "America/Swift_Current"         },
	{ "cst",   0, -21600, "America/Tegucigalpa"           },
	{ "cst",   0, -21600, "America/Thunder_Bay"           },
	{ "cst",   0, -21600, "America/Winnipeg"              },
	{ "cst",   0, -21600, "Canada/Central"                },
	{ "cst",   0, -21600, "Canada/East-Saskatchewan"      },
	{ "cst",   0, -21600, "Canada/Saskatchewan"           },
	{ "cst",   0, -21600, "Mexico/BajaSur"                },
	{ "cst",   0, -21600, "Mexico/General"                },
	{ "cst",   0,  28800, "Asia/Chongqing"                },
	{ "cst",   0,  28800, "Asia/Chungking"                },
	{ "cst",   0,  28800, "Asia/Harbin"                   },
	{ "cst",   0,  28800, "Asia/Macao"                    },
	{ "cst",   0,  28800, "Asia/Macau"                    },
	{ "cst",   0,  28800, "Asia/Shanghai"                 },
	{ "cst",   0,  28800, "Asia/Taipei"                   },
	{ "cst",   0,  28800, "PRC"                           },
	{ "cst",   0,  28800, "ROC"                           },
	{ "cut",   0,   8400, "Europe/Zaporozhye"             },
	{ "cvst",  1,  -3600, "Atlantic/Cape_Verde"           },
	{ "cvt",   0,  -7200, "Atlantic/Cape_Verde"           },
	{ "cvt",   0,  -3600, "Atlantic/Cape_Verde"           },
	{ "cwt",   1, -18000, "America/Chicago"               },
	{ "cwt",   1, -18000, "America/Atikokan"              },
	{ "cwt",   1, -18000, "America/Coral_Harbour"         },
	{ "cwt",   1, -18000, "America/Fort_Wayne"            },
	{ "cwt",   1, -18000, "America/Indiana/Indianapolis"  },
	{ "cwt",   1, -18000, "America/Indiana/Knox"          },
	{ "cwt",   1, -18000, "America/Indiana/Marengo"       },
	{ "cwt",   1, -18000, "America/Indiana/Petersburg"    },
	{ "cwt",   1, -18000, "America/Indiana/Tell_City"     },
	{ "cwt",   1, -18000, "America/Indiana/Vevay"         },
	{ "cwt",   1, -18000, "America/Indiana/Vincennes"     },
	{ "cwt",   1, -18000, "America/Indiana/Winamac"       },
	{ "cwt",   1, -18000, "America/Indianapolis"          },
	{ "cwt",   1, -18000, "America/Kentucky/Louisville"   },
	{ "cwt",   1, -18000, "America/Kentucky/Monticello"   },
	{ "cwt",   1, -18000, "America/Knox_IN"               },
	{ "cwt",   1, -18000, "America/Louisville"            },
	{ "cwt",   1, -18000, "America/Menominee"             },
	{ "cwt",   1, -18000, "America/Mexico_City"           },
	{ "cwt",   1, -18000, "America/Rainy_River"           },
	{ "cwt",   1, -18000, "America/Winnipeg"              },
	{ "cwt",   1, -18000, "Canada/Central"                },
	{ "cwt",   1, -18000, "Mexico/General"                },
	{ "cxt",   0,  25200, "Indian/Christmas"              },
	{ "chst",  0,  36000, "Pacific/Guam"                  },
	{ "chst",  0,  36000, "Pacific/Saipan"                },
	{ "dact",  0,  21600, "Asia/Dacca"                    },
	{ "dact",  0,  21600, "Asia/Dhaka"                    },
	{ "davt",  0,  25200, "Antarctica/Davis"              },
	{ "davt",  0,  18000, "Antarctica/Davis"              },
	{ "ddut",  0,  36000, "Antarctica/DumontDUrville"     },
	{ "dmt",   0,  -1521, "Europe/Dublin"                 },
	{ "dusst", 1,  25200, "Asia/Dushanbe"                 },
	{ "dusst", 1,  21600, "Asia/Dushanbe"                 },
	{ "dust",  0,  21600, "Asia/Dushanbe"                 },
	{ "dust",  0,  18000, "Asia/Dushanbe"                 },
	{ "easst", 1, -21600, "Chile/EasterIsland"            },
	{ "easst", 1, -18000, "Chile/EasterIsland"            },
	{ "easst", 1, -18000, "Pacific/Easter"                },
	{ "easst", 1, -21600, "Pacific/Easter"                },
	{ "east",  0, -21600, "Chile/EasterIsland"            },
	{ "east",  0, -25200, "Chile/EasterIsland"            },
	{ "east",  1,  14400, "Indian/Antananarivo"           },
	{ "east",  0, -21600, "Pacific/Easter"                },
	{ "east",  0, -25200, "Pacific/Easter"                },
	{ "eat",   0,  10800, "Africa/Khartoum"               },
	{ "eat",   0,  10800, "Africa/Addis_Ababa"            },
	{ "eat",   0,  10800, "Africa/Asmara"                 },
	{ "eat",   0,  10800, "Africa/Asmera"                 },
	{ "eat",   0,  10800, "Africa/Dar_es_Salaam"          },
	{ "eat",   0,  10800, "Africa/Djibouti"               },
	{ "eat",   0,  10800, "Africa/Juba"                   },
	{ "eat",   0,  10800, "Africa/Kampala"                },
	{ "eat",   0,  10800, "Africa/Mogadishu"              },
	{ "eat",   0,  10800, "Africa/Nairobi"                },
	{ "eat",   0,  10800, "Indian/Antananarivo"           },
	{ "eat",   0,  10800, "Indian/Comoro"                 },
	{ "eat",   0,  10800, "Indian/Mayotte"                },
	{ "ect",   0, -18000, "America/Guayaquil"             },
	{ "ect",   0, -18000, "Pacific/Galapagos"             },
	{ "eddt",  1, -10800, "America/Iqaluit"               },
	{ "edt",   1, -14400, "America/New_York"              },
	{ "edt",   1, -14400, "America/Cancun"                },
	{ "edt",   1, -14400, "America/Detroit"               },
	{ "edt",   1, -14400, "America/Fort_Wayne"            },
	{ "edt",   1, -14400, "America/Grand_Turk"            },
	{ "edt",   1, -14400, "America/Indiana/Indianapolis"  },
	{ "edt",   1, -14400, "America/Indiana/Marengo"       },
	{ "edt",   1, -14400, "America/Indiana/Petersburg"    },
	{ "edt",   1, -14400, "America/Indiana/Tell_City"     },
	{ "edt",   1, -14400, "America/Indiana/Vevay"         },
	{ "edt",   1, -14400, "America/Indiana/Vincennes"     },
	{ "edt",   1, -14400, "America/Indiana/Winamac"       },
	{ "edt",   1, -14400, "America/Indianapolis"          },
	{ "edt",   1, -14400, "America/Iqaluit"               },
	{ "edt",   1, -14400, "America/Jamaica"               },
	{ "edt",   1, -14400, "America/Kentucky/Louisville"   },
	{ "edt",   1, -14400, "America/Kentucky/Monticello"   },
	{ "edt",   1, -14400, "America/Louisville"            },
	{ "edt",   1, -14400, "America/Montreal"              },
	{ "edt",   1, -14400, "America/Nassau"                },
	{ "edt",   1, -14400, "America/Nipigon"               },
	{ "edt",   1, -14400, "America/Pangnirtung"           },
	{ "edt",   1, -14400, "America/Port-au-Prince"        },
	{ "edt",   1, -14400, "America/Santo_Domingo"         },
	{ "edt",   1, -14400, "America/Thunder_Bay"           },
	{ "edt",   1, -14400, "America/Toronto"               },
	{ "edt",   1, -14400, "Canada/Eastern"                },
	{ "edt",   1, -14400, "EST"                           },
	{ "eest",  1,  10800, "Europe/Helsinki"               },
	{ "eest",  1,  10800, "Africa/Cairo"                  },
	{ "eest",  1,  10800, "Asia/Amman"                    },
	{ "eest",  1,  10800, "Asia/Beirut"                   },
	{ "eest",  1,  10800, "Asia/Damascus"                 },
	{ "eest",  1,  10800, "Asia/Gaza"                     },
	{ "eest",  1,  10800, "Asia/Hebron"                   },
	{ "eest",  1,  10800, "Asia/Istanbul"                 },
	{ "eest",  1,  10800, "Asia/Nicosia"                  },
	{ "eest",  1,  10800, "EET"                           },
	{ "eest",  1,  10800, "Europe/Athens"                 },
	{ "eest",  1,  10800, "Europe/Bucharest"              },
	{ "eest",  1,  10800, "Europe/Chisinau"               },
	{ "eest",  1,  10800, "Europe/Istanbul"               },
	{ "eest",  1,  10800, "Europe/Kaliningrad"            },
	{ "eest",  1,  10800, "Europe/Kiev"                   },
	{ "eest",  1,  10800, "Europe/Mariehamn"              },
	{ "eest",  1,  10800, "Europe/Minsk"                  },
	{ "eest",  1,  10800, "Europe/Moscow"                 },
	{ "eest",  1,  10800, "Europe/Nicosia"                },
	{ "eest",  1,  10800, "Europe/Riga"                   },
	{ "eest",  1,  10800, "Europe/Samara"                 },
	{ "eest",  1,  10800, "Europe/Simferopol"             },
	{ "eest",  1,  10800, "Europe/Sofia"                  },
	{ "eest",  1,  10800, "Europe/Tallinn"                },
	{ "eest",  1,  10800, "Europe/Tiraspol"               },
	{ "eest",  1,  10800, "Europe/Uzhgorod"               },
	{ "eest",  1,  10800, "Europe/Vilnius"                },
	{ "eest",  1,  10800, "Europe/Warsaw"                 },
	{ "eest",  1,  10800, "Europe/Zaporozhye"             },
	{ "eet",   0,   7200, "Europe/Helsinki"               },
	{ "eet",   1,  10800, "Asia/Gaza"                     },
	{ "eet",   1,  10800, "Asia/Hebron"                   },
	{ "eet",   0,   7200, "Africa/Cairo"                  },
	{ "eet",   0,   7200, "Africa/Tripoli"                },
	{ "eet",   0,   7200, "Asia/Amman"                    },
	{ "eet",   0,   7200, "Asia/Beirut"                   },
	{ "eet",   0,   7200, "Asia/Damascus"                 },
	{ "eet",   0,   7200, "Asia/Gaza"                     },
	{ "eet",   0,   7200, "Asia/Hebron"                   },
	{ "eet",   0,   7200, "Asia/Istanbul"                 },
	{ "eet",   0,   7200, "Asia/Nicosia"                  },
	{ "eet",   0,   7200, "EET"                           },
	{ "eet",   0,   7200, "Europe/Athens"                 },
	{ "eet",   0,   7200, "Europe/Bucharest"              },
	{ "eet",   0,   7200, "Europe/Chisinau"               },
	{ "eet",   0,   7200, "Europe/Istanbul"               },
	{ "eet",   0,   7200, "Europe/Kaliningrad"            },
	{ "eet",   0,   7200, "Europe/Kiev"                   },
	{ "eet",   0,   7200, "Europe/Mariehamn"              },
	{ "eet",   0,   7200, "Europe/Minsk"                  },
	{ "eet",   0,   7200, "Europe/Moscow"                 },
	{ "eet",   0,   7200, "Europe/Nicosia"                },
	{ "eet",   0,   7200, "Europe/Riga"                   },
	{ "eet",   0,   7200, "Europe/Simferopol"             },
	{ "eet",   0,   7200, "Europe/Sofia"                  },
	{ "eet",   0,   7200, "Europe/Tallinn"                },
	{ "eet",   0,   7200, "Europe/Tiraspol"               },
	{ "eet",   0,   7200, "Europe/Uzhgorod"               },
	{ "eet",   0,   7200, "Europe/Vilnius"                },
	{ "eet",   0,   7200, "Europe/Warsaw"                 },
	{ "eet",   0,   7200, "Europe/Zaporozhye"             },
	{ "egst",  1,      0, "America/Scoresbysund"          },
	{ "egt",   0,  -3600, "America/Scoresbysund"          },
	{ "ehdt",  1, -16200, "America/Santo_Domingo"         },
	{ "emt",   0, -26248, "Chile/EasterIsland"            },
	{ "emt",   0, -26248, "Pacific/Easter"                },
	{ "ept",   1, -14400, "America/New_York"              },
	{ "ept",   1, -14400, "America/Detroit"               },
	{ "ept",   1, -14400, "America/Iqaluit"               },
	{ "ept",   1, -14400, "America/Montreal"              },
	{ "ept",   1, -14400, "America/Nipigon"               },
	{ "ept",   1, -14400, "America/Thunder_Bay"           },
	{ "ept",   1, -14400, "America/Toronto"               },
	{ "ept",   1, -14400, "Canada/Eastern"                },
	{ "ept",   1, -14400, "EST"                           },
	{ "est",   0, -18000, "America/New_York"              },
	{ "est",   0, -18000, "America/Antigua"               },
	{ "est",   0, -18000, "America/Atikokan"              },
	{ "est",   0, -18000, "America/Cambridge_Bay"         },
	{ "est",   0, -18000, "America/Cancun"                },
	{ "est",   0, -18000, "America/Cayman"                },
	{ "est",   0, -18000, "America/Chicago"               },
	{ "est",   0, -18000, "America/Coral_Harbour"         },
	{ "est",   0, -18000, "America/Detroit"               },
	{ "est",   0, -18000, "America/Fort_Wayne"            },
	{ "est",   0, -18000, "America/Grand_Turk"            },
	{ "est",   0, -18000, "America/Indiana/Indianapolis"  },
	{ "est",   0, -18000, "America/Indiana/Knox"          },
	{ "est",   0, -18000, "America/Indiana/Marengo"       },
	{ "est",   0, -18000, "America/Indiana/Petersburg"    },
	{ "est",   0, -18000, "America/Indiana/Tell_City"     },
	{ "est",   0, -18000, "America/Indiana/Vevay"         },
	{ "est",   0, -18000, "America/Indiana/Vincennes"     },
	{ "est",   0, -18000, "America/Indiana/Winamac"       },
	{ "est",   0, -18000, "America/Indianapolis"          },
	{ "est",   0, -18000, "America/Iqaluit"               },
	{ "est",   0, -18000, "America/Jamaica"               },
	{ "est",   0, -18000, "America/Kentucky/Louisville"   },
	{ "est",   0, -18000, "America/Kentucky/Monticello"   },
	{ "est",   0, -18000, "America/Knox_IN"               },
	{ "est",   0, -18000, "America/Louisville"            },
	{ "est",   0, -18000, "America/Managua"               },
	{ "est",   0, -18000, "America/Menominee"             },
	{ "est",   0, -18000, "America/Merida"                },
	{ "est",   0, -18000, "America/Moncton"               },
	{ "est",   0, -18000, "America/Montreal"              },
	{ "est",   0, -18000, "America/Nassau"                },
	{ "est",   0, -18000, "America/Nipigon"               },
	{ "est",   0, -18000, "America/Panama"                },
	{ "est",   0, -18000, "America/Pangnirtung"           },
	{ "est",   0, -18000, "America/Port-au-Prince"        },
	{ "est",   0, -18000, "America/Rankin_Inlet"          },
	{ "est",   0, -18000, "America/Resolute"              },
	{ "est",   0, -18000, "America/Santo_Domingo"         },
	{ "est",   0, -18000, "America/Thunder_Bay"           },
	{ "est",   0, -18000, "America/Toronto"               },
	{ "est",   0, -18000, "Canada/Eastern"                },
	{ "est",   0, -18000, "EST"                           },
	{ "ewt",   1, -14400, "America/New_York"              },
	{ "ewt",   1, -14400, "America/Detroit"               },
	{ "ewt",   1, -14400, "America/Iqaluit"               },
	{ "ewt",   1, -14400, "America/Montreal"              },
	{ "ewt",   1, -14400, "America/Nipigon"               },
	{ "ewt",   1, -14400, "America/Thunder_Bay"           },
	{ "ewt",   1, -14400, "America/Toronto"               },
	{ "ewt",   1, -14400, "Canada/Eastern"                },
	{ "ewt",   1, -14400, "EST"                           },
	{ "fet",   0,  10800, "Europe/Kaliningrad"            },
	{ "fet",   0,  10800, "Europe/Minsk"                  },
	{ "ffmt",  0, -14660, "America/Martinique"            },
	{ "fjst",  1,  46800, "Pacific/Fiji"                  },
	{ "fjt",   0,  43200, "Pacific/Fiji"                  },
	{ "fkst",  1,  -7200, "Atlantic/Stanley"              },
	{ "fkst",  0, -10800, "Atlantic/Stanley"              },
	{ "fkst",  1, -10800, "Atlantic/Stanley"              },
	{ "fkt",   0, -14400, "Atlantic/Stanley"              },
	{ "fkt",   0, -10800, "Atlantic/Stanley"              },
	{ "fmt",   0,  -4056, "Atlantic/Madeira"              },
	{ "fnst",  1,  -3600, "America/Noronha"               },
	{ "fnst",  1,  -3600, "Brazil/DeNoronha"              },
	{ "fnt",   0,  -7200, "America/Noronha"               },
	{ "fnt",   0,  -7200, "Brazil/DeNoronha"              },
	{ "fort",  0,  18000, "Asia/Aqtau"                    },
	{ "fort",  0,  14400, "Asia/Aqtau"                    },
	{ "frust", 1,  25200, "Asia/Bishkek"                  },
	{ "frust", 1,  21600, "Asia/Bishkek"                  },
	{ "frut",  0,  21600, "Asia/Bishkek"                  },
	{ "frut",  0,  18000, "Asia/Bishkek"                  },
	{ "galt",  0, -21600, "Pacific/Galapagos"             },
	{ "gamt",  0, -32400, "Pacific/Gambier"               },
	{ "gbgt",  0, -13500, "America/Guyana"                },
	{ "gest",  1,  14400, "Asia/Tbilisi"                  },
	{ "gest",  1,  18000, "Asia/Tbilisi"                  },
	{ "get",   0,  14400, "Asia/Tbilisi"                  },
	{ "get",   0,  10800, "Asia/Tbilisi"                  },
	{ "gft",   0, -14400, "America/Cayenne"               },
	{ "gft",   0, -10800, "America/Cayenne"               },
	{ "ghst",  1,   1200, "Africa/Accra"                  },
	{ "gilt",  0,  43200, "Pacific/Tarawa"                },
	{ "gmt",   0,      0, "Africa/Abidjan"                },
	{ "gmt",   0,      0, "Africa/Accra"                  },
	{ "gmt",   0,      0, "Africa/Bamako"                 },
	{ "gmt",   0,      0, "Africa/Banjul"                 },
	{ "gmt",   0,      0, "Africa/Bissau"                 },
	{ "gmt",   0,      0, "Africa/Conakry"                },
	{ "gmt",   0,      0, "Africa/Dakar"                  },
	{ "gmt",   0,      0, "Africa/Freetown"               },
	{ "gmt",   0,      0, "Africa/Lome"                   },
	{ "gmt",   0,      0, "Africa/Malabo"                 },
	{ "gmt",   0,      0, "Africa/Monrovia"               },
	{ "gmt",   0,      0, "Africa/Niamey"                 },
	{ "gmt",   0,      0, "Africa/Nouakchott"             },
	{ "gmt",   0,      0, "Africa/Ouagadougou"            },
	{ "gmt",   0,      0, "Africa/Porto-Novo"             },
	{ "gmt",   0,      0, "Africa/Sao_Tome"               },
	{ "gmt",   0,      0, "Africa/Timbuktu"               },
	{ "gmt",   0,      0, "America/Danmarkshavn"          },
	{ "gmt",   0,      0, "Atlantic/Reykjavik"            },
	{ "gmt",   0,      0, "Atlantic/St_Helena"            },
	{ "gmt",   0,      0, "Etc/GMT"                       },
	{ "gmt",   0,      0, "Etc/Greenwich"                 },
	{ "gmt",   0,      0, "Europe/Belfast"                },
	{ "gmt",   0,      0, "Europe/Dublin"                 },
	{ "gmt",   0,      0, "Europe/Gibraltar"              },
	{ "gmt",   0,      0, "Europe/Guernsey"               },
	{ "gmt",   0,      0, "Europe/Isle_of_Man"            },
	{ "gmt",   0,      0, "Europe/Jersey"                 },
	{ "gmt",   0,      0, "Europe/London"                 },
	{ "gmt",   0,      0, "GB"                            },
	{ "gst",   0,  14400, "Asia/Dubai"                    },
	{ "gst",   0,  -7200, "Atlantic/South_Georgia"        },
	{ "gst",   0,  14400, "Asia/Bahrain"                  },
	{ "gst",   0,  14400, "Asia/Muscat"                   },
	{ "gst",   0,  14400, "Asia/Qatar"                    },
	{ "gst",   0,  36000, "Pacific/Guam"                  },
	{ "gyt",   0, -14400, "America/Guyana"                },
	{ "gyt",   0, -10800, "America/Guyana"                },
	{ "gyt",   0, -13500, "America/Guyana"                },
	{ "hadt",  1, -32400, "America/Adak"                  },
	{ "hadt",  1, -32400, "America/Atka"                  },
	{ "hast",  0, -36000, "America/Adak"                  },
	{ "hast",  0, -36000, "America/Atka"                  },
	{ "hdt",   1, -34200, "Pacific/Honolulu"              },
	{ "hdt",   1, -34200, "HST"                           },
	{ "hdt",   1, -34200, "Pacific/Johnston"              },
	{ "hkst",  1,  32400, "Asia/Hong_Kong"                },
	{ "hkt",   0,  28800, "Asia/Hong_Kong"                },
	{ "hmt",   0, -19776, "America/Havana"                },
	{ "hmt",   0,  -6872, "Atlantic/Azores"               },
	{ "hmt",   0,  21200, "Asia/Calcutta"                 },
	{ "hmt",   0,  21200, "Asia/Dacca"                    },
	{ "hmt",   0,  21200, "Asia/Dhaka"                    },
	{ "hmt",   0,  21200, "Asia/Kolkata"                  },
	{ "hmt",   0,   5989, "EET"                           },
	{ "hmt",   0,   5989, "Europe/Helsinki"               },
	{ "hmt",   0,   5989, "Europe/Mariehamn"              },
	{ "hovst", 1,  28800, "Asia/Hovd"                     },
	{ "hovt",  0,  25200, "Asia/Hovd"                     },
	{ "hovt",  0,  21600, "Asia/Hovd"                     },
	{ "hst",   0, -36000, "Pacific/Honolulu"              },
	{ "hst",   0, -37800, "Pacific/Honolulu"              },
	{ "hst",   0, -36000, "HST"                           },
	{ "hst",   0, -36000, "Pacific/Johnston"              },
	{ "hst",   0, -37800, "HST"                           },
	{ "hst",   0, -37800, "Pacific/Johnston"              },
	{ "ict",   0,  25200, "Asia/Bangkok"                  },
	{ "ict",   0,  25200, "Asia/Ho_Chi_Minh"              },
	{ "ict",   0,  25200, "Asia/Phnom_Penh"               },
	{ "ict",   0,  25200, "Asia/Saigon"                   },
	{ "ict",   0,  25200, "Asia/Vientiane"                },
	{ "ict",   0,  28800, "Asia/Ho_Chi_Minh"              },
	{ "ict",   0,  28800, "Asia/Phnom_Penh"               },
	{ "ict",   0,  28800, "Asia/Saigon"                   },
	{ "ict",   0,  28800, "Asia/Vientiane"                },
	{ "iddt",  1,  14400, "Asia/Jerusalem"                },
	{ "iddt",  1,  14400, "Asia/Tel_Aviv"                 },
	{ "idt",   1,  10800, "Asia/Jerusalem"                },
	{ "idt",   1,  10800, "Asia/Gaza"                     },
	{ "idt",   1,  10800, "Asia/Hebron"                   },
	{ "idt",   1,  10800, "Asia/Tel_Aviv"                 },
	{ "ihst",  1,  21600, "Asia/Colombo"                  },
	{ "imt",   0,  25035, "Asia/Irkutsk"                  },
	{ "imt",   0,   7016, "Asia/Istanbul"                 },
	{ "imt",   0,   7016, "Europe/Istanbul"               },
	{ "iot",   0,  21600, "Indian/Chagos"                 },
	{ "iot",   0,  18000, "Indian/Chagos"                 },
	{ "irdt",  1,  16200, "Asia/Tehran"                   },
	{ "irdt",  1,  18000, "Asia/Tehran"                   },
	{ "irkst", 1,  32400, "Asia/Irkutsk"                  },
	{ "irkst", 1,  28800, "Asia/Irkutsk"                  },
	{ "irkt",  0,  28800, "Asia/Irkutsk"                  },
	{ "irkt",  0,  25200, "Asia/Irkutsk"                  },
	{ "irkt",  0,  32400, "Asia/Irkutsk"                  },
	{ "irkt",  0,  28800, "Asia/Chita"                    },
	{ "irst",  0,  12600, "Asia/Tehran"                   },
	{ "irst",  0,  14400, "Asia/Tehran"                   },
	{ "isst",  1,      0, "Atlantic/Reykjavik"            },
	{ "ist",   0,   7200, "Asia/Jerusalem"                },
	{ "ist",   0,  -3600, "Atlantic/Reykjavik"            },
	{ "ist",   0,  19800, "Asia/Calcutta"                 },
	{ "ist",   0,  19800, "Asia/Colombo"                  },
	{ "ist",   0,  19800, "Asia/Dacca"                    },
	{ "ist",   0,  19800, "Asia/Dhaka"                    },
	{ "ist",   0,  19800, "Asia/Karachi"                  },
	{ "ist",   0,  19800, "Asia/Kathmandu"                },
	{ "ist",   0,  19800, "Asia/Katmandu"                 },
	{ "ist",   0,  19800, "Asia/Kolkata"                  },
	{ "ist",   0,  19800, "Asia/Thimbu"                   },
	{ "ist",   0,  19800, "Asia/Thimphu"                  },
	{ "ist",   1,   2079, "Europe/Dublin"                 },
	{ "ist",   1,  23400, "Asia/Calcutta"                 },
	{ "ist",   1,  23400, "Asia/Colombo"                  },
	{ "ist",   1,  23400, "Asia/Karachi"                  },
	{ "ist",   1,  23400, "Asia/Kolkata"                  },
	{ "ist",   0,   3600, "Europe/Dublin"                 },
	{ "ist",   1,   3600, "Europe/Dublin"                 },
	{ "ist",   0,   7200, "Asia/Gaza"                     },
	{ "ist",   0,   7200, "Asia/Hebron"                   },
	{ "ist",   0,   7200, "Asia/Tel_Aviv"                 },
	{ "javt",  0,  26400, "Asia/Jakarta"                  },
	{ "jcst",  0,  32400, "Asia/Pyongyang"                },
	{ "jcst",  0,  32400, "Asia/Sakhalin"                 },
	{ "jcst",  0,  32400, "Asia/Seoul"                    },
	{ "jcst",  0,  32400, "Asia/Tokyo"                    },
	{ "jcst",  0,  32400, "ROK"                           },
	{ "jdt",   1,  36000, "Asia/Tokyo"                    },
	{ "jmt",   0,   8440, "Asia/Jerusalem"                },
	{ "jmt",   0,   8440, "Asia/Tel_Aviv"                 },
	{ "jst",   0,  32400, "Asia/Tokyo"                    },
	{ "jst",   0,  32400, "Asia/Dili"                     },
	{ "jst",   0,  32400, "Asia/Hong_Kong"                },
	{ "jst",   0,  32400, "Asia/Jakarta"                  },
	{ "jst",   0,  32400, "Asia/Kuala_Lumpur"             },
	{ "jst",   0,  32400, "Asia/Kuching"                  },
	{ "jst",   0,  32400, "Asia/Makassar"                 },
	{ "jst",   0,  32400, "Asia/Manila"                   },
	{ "jst",   0,  32400, "Asia/Pontianak"                },
	{ "jst",   0,  32400, "Asia/Pyongyang"                },
	{ "jst",   0,  32400, "Asia/Rangoon"                  },
	{ "jst",   0,  32400, "Asia/Sakhalin"                 },
	{ "jst",   0,  32400, "Asia/Seoul"                    },
	{ "jst",   0,  32400, "Asia/Singapore"                },
	{ "jst",   0,  32400, "Asia/Taipei"                   },
	{ "jst",   0,  32400, "Asia/Ujung_Pandang"            },
	{ "jst",   0,  32400, "Pacific/Nauru"                 },
	{ "jst",   0,  32400, "ROC"                           },
	{ "jst",   0,  32400, "ROK"                           },
	{ "jwst",  0,  28800, "Asia/Taipei"                   },
	{ "jwst",  0,  28800, "ROC"                           },
	{ "kart",  0,  18000, "Asia/Karachi"                  },
	{ "kdt",   1,  36000, "Asia/Seoul"                    },
	{ "kdt",   1,  32400, "Asia/Seoul"                    },
	{ "kdt",   1,  32400, "ROK"                           },
	{ "kdt",   1,  36000, "ROK"                           },
	{ "kgst",  1,  21600, "Asia/Bishkek"                  },
	{ "kgt",   0,  18000, "Asia/Bishkek"                  },
	{ "kgt",   0,  21600, "Asia/Bishkek"                  },
	{ "kizst", 1,  21600, "Asia/Qyzylorda"                },
	{ "kizt",  0,  21600, "Asia/Qyzylorda"                },
	{ "kizt",  0,  14400, "Asia/Qyzylorda"                },
	{ "kizt",  0,  18000, "Asia/Qyzylorda"                },
	{ "kmt",   0,   5736, "Europe/Vilnius"                },
	{ "kmt",   0, -18431, "America/Cayman"                },
	{ "kmt",   0, -18431, "America/Grand_Turk"            },
	{ "kmt",   0, -18431, "America/Jamaica"               },
	{ "kmt",   0,   7324, "Europe/Kiev"                   },
	{ "kost",  0,  43200, "Pacific/Kosrae"                },
	{ "kost",  0,  39600, "Pacific/Kosrae"                },
	{ "krast", 1,  28800, "Asia/Krasnoyarsk"              },
	{ "krast", 1,  25200, "Asia/Krasnoyarsk"              },
	{ "krast", 1,  25200, "Asia/Novokuznetsk"             },
	{ "krast", 1,  28800, "Asia/Novokuznetsk"             },
	{ "krat",  0,  25200, "Asia/Krasnoyarsk"              },
	{ "krat",  0,  21600, "Asia/Krasnoyarsk"              },
	{ "krat",  0,  28800, "Asia/Krasnoyarsk"              },
	{ "krat",  0,  21600, "Asia/Novokuznetsk"             },
	{ "krat",  0,  25200, "Asia/Novokuznetsk"             },
	{ "kst",   0,  28800, "Asia/Seoul"                    },
	{ "kst",   0,  30600, "Asia/Seoul"                    },
	{ "kst",   0,  32400, "Asia/Pyongyang"                },
	{ "kst",   0,  32400, "Asia/Seoul"                    },
	{ "kst",   0,  28800, "Asia/Pyongyang"                },
	{ "kst",   0,  28800, "ROK"                           },
	{ "kst",   0,  30600, "Asia/Pyongyang"                },
	{ "kst",   0,  30600, "ROK"                           },
	{ "kst",   0,  32400, "ROK"                           },
	{ "kuyst", 1,  18000, "Europe/Samara"                 },
	{ "kuyt",  0,  14400, "Europe/Samara"                 },
	{ "kuyt",  0,  10800, "Europe/Samara"                 },
	{ "kwat",  0, -43200, "Pacific/Kwajalein"             },
	{ "lhdt",  1,  39600, "Australia/LHI"                 },
	{ "lhdt",  1,  39600, "Australia/Lord_Howe"           },
	{ "lhdt",  1,  41400, "Australia/LHI"                 },
	{ "lhdt",  1,  41400, "Australia/Lord_Howe"           },
	{ "lhst",  0,  37800, "Australia/Lord_Howe"           },
	{ "lhst",  0,  37800, "Australia/LHI"                 },
	{ "lint",  0,  50400, "Pacific/Kiritimati"            },
	{ "lint",  0, -36000, "Pacific/Kiritimati"            },
	{ "lint",  0, -38400, "Pacific/Kiritimati"            },
	{ "lkt",   0,  23400, "Asia/Colombo"                  },
	{ "lkt",   0,  21600, "Asia/Colombo"                  },
	{ "lrt",   0,  -2670, "Africa/Monrovia"               },
	{ "lst",   1,   9388, "Europe/Riga"                   },
	{ "madmt", 1,   3600, "Atlantic/Madeira"              },
	{ "madst", 1,      0, "Atlantic/Madeira"              },
	{ "madt",  0,  -3600, "Atlantic/Madeira"              },
	{ "magst", 1,  43200, "Asia/Magadan"                  },
	{ "magst", 1,  39600, "Asia/Magadan"                  },
	{ "magst", 1,  39600, "Asia/Srednekolymsk"            },
	{ "magst", 1,  39600, "Asia/Ust-Nera"                 },
	{ "magst", 1,  43200, "Asia/Srednekolymsk"            },
	{ "magst", 1,  43200, "Asia/Ust-Nera"                 },
	{ "magt",  0,  36000, "Asia/Magadan"                  },
	{ "magt",  0,  39600, "Asia/Magadan"                  },
	{ "magt",  0,  43200, "Asia/Magadan"                  },
	{ "magt",  0,  36000, "Asia/Srednekolymsk"            },
	{ "magt",  0,  36000, "Asia/Ust-Nera"                 },
	{ "magt",  0,  39600, "Asia/Srednekolymsk"            },
	{ "magt",  0,  39600, "Asia/Ust-Nera"                 },
	{ "magt",  0,  43200, "Asia/Srednekolymsk"            },
	{ "magt",  0,  43200, "Asia/Ust-Nera"                 },
	{ "malst", 1,  26400, "Asia/Singapore"                },
	{ "malst", 1,  26400, "Asia/Kuala_Lumpur"             },
	{ "malt",  0,  27000, "Asia/Singapore"                },
	{ "malt",  0,  25200, "Asia/Singapore"                },
	{ "malt",  0,  26400, "Asia/Singapore"                },
	{ "malt",  0,  25200, "Asia/Kuala_Lumpur"             },
	{ "malt",  0,  26400, "Asia/Kuala_Lumpur"             },
	{ "malt",  0,  27000, "Asia/Kuala_Lumpur"             },
	{ "mart",  0, -34200, "Pacific/Marquesas"             },
	{ "mawt",  0,  21600, "Antarctica/Mawson"             },
	{ "mawt",  0,  18000, "Antarctica/Mawson"             },
	{ "mddt",  1, -18000, "America/Cambridge_Bay"         },
	{ "mddt",  1, -18000, "America/Yellowknife"           },
	{ "mdst",  1,  16279, "Europe/Moscow"                 },
	{ "mdt",   1, -21600, "America/Denver"                },
	{ "mdt",   1, -21600, "America/Bahia_Banderas"        },
	{ "mdt",   1, -21600, "America/Boise"                 },
	{ "mdt",   1, -21600, "America/Cambridge_Bay"         },
	{ "mdt",   1, -21600, "America/Chihuahua"             },
	{ "mdt",   1, -21600, "America/Edmonton"              },
	{ "mdt",   1, -21600, "America/Hermosillo"            },
	{ "mdt",   1, -21600, "America/Inuvik"                },
	{ "mdt",   1, -21600, "America/Mazatlan"              },
	{ "mdt",   1, -21600, "America/North_Dakota/Beulah"   },
	{ "mdt",   1, -21600, "America/North_Dakota/Center"   },
	{ "mdt",   1, -21600, "America/North_Dakota/New_Salem" },
	{ "mdt",   1, -21600, "America/Ojinaga"               },
	{ "mdt",   1, -21600, "America/Phoenix"               },
	{ "mdt",   1, -21600, "America/Regina"                },
	{ "mdt",   1, -21600, "America/Shiprock"              },
	{ "mdt",   1, -21600, "America/Swift_Current"         },
	{ "mdt",   1, -21600, "America/Yellowknife"           },
	{ "mdt",   1, -21600, "Canada/East-Saskatchewan"      },
	{ "mdt",   1, -21600, "Canada/Mountain"               },
	{ "mdt",   1, -21600, "Canada/Saskatchewan"           },
	{ "mdt",   1, -21600, "Mexico/BajaSur"                },
	{ "mdt",   1, -21600, "MST"                           },
	{ "mest",  1,   7200, "MET"                           },
	{ "met",   0,   3600, "MET"                           },
	{ "mht",   0,  43200, "Pacific/Kwajalein"             },
	{ "mht",   0,  39600, "Pacific/Kwajalein"             },
	{ "mht",   0,  39600, "Pacific/Majuro"                },
	{ "mht",   0,  43200, "Pacific/Majuro"                },
	{ "mist",  0,  39600, "Antarctica/Macquarie"          },
	{ "mmt",   0,   9017, "Europe/Moscow"                 },
	{ "mmt",   0,   9079, "Europe/Moscow"                 },
	{ "mmt",   0, -13484, "America/Montevideo"            },
	{ "mmt",   0, -20712, "America/Managua"               },
	{ "mmt",   0,  -2588, "Africa/Monrovia"               },
	{ "mmt",   0,  17640, "Indian/Maldives"               },
	{ "mmt",   0,  19172, "Asia/Colombo"                  },
	{ "mmt",   0,  23400, "Asia/Rangoon"                  },
	{ "mmt",   0,  28656, "Asia/Makassar"                 },
	{ "mmt",   0,  28656, "Asia/Ujung_Pandang"            },
	{ "mmt",   0,   6600, "Europe/Minsk"                  },
	{ "most",  1,  32400, "Asia/Macao"                    },
	{ "most",  1,  32400, "Asia/Macau"                    },
	{ "mot",   0,  28800, "Asia/Macao"                    },
	{ "mot",   0,  28800, "Asia/Macau"                    },
	{ "mpt",   1, -21600, "America/Denver"                },
	{ "mpt",   0,  36000, "Pacific/Saipan"                },
	{ "mpt",   1, -21600, "America/Boise"                 },
	{ "mpt",   1, -21600, "America/Cambridge_Bay"         },
	{ "mpt",   1, -21600, "America/Edmonton"              },
	{ "mpt",   1, -21600, "America/North_Dakota/Beulah"   },
	{ "mpt",   1, -21600, "America/North_Dakota/Center"   },
	{ "mpt",   1, -21600, "America/North_Dakota/New_Salem" },
	{ "mpt",   1, -21600, "America/Regina"                },
	{ "mpt",   1, -21600, "America/Shiprock"              },
	{ "mpt",   1, -21600, "America/Swift_Current"         },
	{ "mpt",   1, -21600, "America/Yellowknife"           },
	{ "mpt",   1, -21600, "Canada/East-Saskatchewan"      },
	{ "mpt",   1, -21600, "Canada/Mountain"               },
	{ "mpt",   1, -21600, "Canada/Saskatchewan"           },
	{ "mpt",   1, -21600, "MST"                           },
	{ "mpt",   0,  32400, "Pacific/Saipan"                },
	{ "msd",   1,  14400, "Europe/Moscow"                 },
	{ "msd",   1,  14400, "Europe/Chisinau"               },
	{ "msd",   1,  14400, "Europe/Kaliningrad"            },
	{ "msd",   1,  14400, "Europe/Kiev"                   },
	{ "msd",   1,  14400, "Europe/Minsk"                  },
	{ "msd",   1,  14400, "Europe/Riga"                   },
	{ "msd",   1,  14400, "Europe/Samara"                 },
	{ "msd",   1,  14400, "Europe/Simferopol"             },
	{ "msd",   1,  14400, "Europe/Tallinn"                },
	{ "msd",   1,  14400, "Europe/Tiraspol"               },
	{ "msd",   1,  14400, "Europe/Uzhgorod"               },
	{ "msd",   1,  14400, "Europe/Vilnius"                },
	{ "msd",   1,  14400, "Europe/Zaporozhye"             },
	{ "msk",   0,  10800, "Europe/Moscow"                 },
	{ "msk",   0,  14400, "Europe/Moscow"                 },
	{ "msk",   0,  10800, "Europe/Chisinau"               },
	{ "msk",   0,  10800, "Europe/Kaliningrad"            },
	{ "msk",   0,  10800, "Europe/Kiev"                   },
	{ "msk",   0,  10800, "Europe/Minsk"                  },
	{ "msk",   0,  10800, "Europe/Riga"                   },
	{ "msk",   0,  10800, "Europe/Samara"                 },
	{ "msk",   0,  10800, "Europe/Simferopol"             },
	{ "msk",   0,  10800, "Europe/Tallinn"                },
	{ "msk",   0,  10800, "Europe/Tiraspol"               },
	{ "msk",   0,  10800, "Europe/Uzhgorod"               },
	{ "msk",   0,  10800, "Europe/Vilnius"                },
	{ "msk",   0,  10800, "Europe/Volgograd"              },
	{ "msk",   0,  10800, "Europe/Zaporozhye"             },
	{ "msk",   0,  14400, "Europe/Simferopol"             },
	{ "msk",   0,  14400, "Europe/Volgograd"              },
	{ "msk",   1,  14400, "Europe/Volgograd"              },
	{ "msm",   1,  18000, "Europe/Moscow"                 },
	{ "mst",   0, -25200, "America/Denver"                },
	{ "mst",   0, -25200, "America/Bahia_Banderas"        },
	{ "mst",   0, -25200, "America/Boise"                 },
	{ "mst",   0, -25200, "America/Cambridge_Bay"         },
	{ "mst",   0, -25200, "America/Chihuahua"             },
	{ "mst",   0, -25200, "America/Creston"               },
	{ "mst",   0, -25200, "America/Dawson_Creek"          },
	{ "mst",   0, -25200, "America/Edmonton"              },
	{ "mst",   0, -25200, "America/Ensenada"              },
	{ "mst",   0, -25200, "America/Hermosillo"            },
	{ "mst",   0, -25200, "America/Inuvik"                },
	{ "mst",   0, -25200, "America/Mazatlan"              },
	{ "mst",   0, -25200, "America/Mexico_City"           },
	{ "mst",   0, -25200, "America/North_Dakota/Beulah"   },
	{ "mst",   0, -25200, "America/North_Dakota/Center"   },
	{ "mst",   0, -25200, "America/North_Dakota/New_Salem" },
	{ "mst",   0, -25200, "America/Ojinaga"               },
	{ "mst",   0, -25200, "America/Phoenix"               },
	{ "mst",   0, -25200, "America/Regina"                },
	{ "mst",   0, -25200, "America/Santa_Isabel"          },
	{ "mst",   0, -25200, "America/Shiprock"              },
	{ "mst",   0, -25200, "America/Swift_Current"         },
	{ "mst",   0, -25200, "America/Tijuana"               },
	{ "mst",   0, -25200, "America/Yellowknife"           },
	{ "mst",   0, -25200, "Canada/East-Saskatchewan"      },
	{ "mst",   0, -25200, "Canada/Mountain"               },
	{ "mst",   0, -25200, "Canada/Saskatchewan"           },
	{ "mst",   0, -25200, "Mexico/BajaNorte"              },
	{ "mst",   0, -25200, "Mexico/BajaSur"                },
	{ "mst",   0, -25200, "Mexico/General"                },
	{ "mst",   0, -25200, "MST"                           },
	{ "mst",   1,  12679, "Europe/Moscow"                 },
	{ "must",  1,  18000, "Indian/Mauritius"              },
	{ "mut",   0,  14400, "Indian/Mauritius"              },
	{ "mvt",   0,  18000, "Indian/Maldives"               },
	{ "mwt",   1, -21600, "America/Denver"                },
	{ "mwt",   1, -21600, "America/Boise"                 },
	{ "mwt",   1, -21600, "America/Cambridge_Bay"         },
	{ "mwt",   1, -21600, "America/Edmonton"              },
	{ "mwt",   1, -21600, "America/North_Dakota/Beulah"   },
	{ "mwt",   1, -21600, "America/North_Dakota/Center"   },
	{ "mwt",   1, -21600, "America/North_Dakota/New_Salem" },
	{ "mwt",   1, -21600, "America/Phoenix"               },
	{ "mwt",   1, -21600, "America/Regina"                },
	{ "mwt",   1, -21600, "America/Shiprock"              },
	{ "mwt",   1, -21600, "America/Swift_Current"         },
	{ "mwt",   1, -21600, "America/Yellowknife"           },
	{ "mwt",   1, -21600, "Canada/East-Saskatchewan"      },
	{ "mwt",   1, -21600, "Canada/Mountain"               },
	{ "mwt",   1, -21600, "Canada/Saskatchewan"           },
	{ "mwt",   1, -21600, "MST"                           },
	{ "myt",   0,  28800, "Asia/Kuala_Lumpur"             },
	{ "myt",   0,  28800, "Asia/Kuching"                  },
	{ "ncst",  1,  43200, "Pacific/Noumea"                },
	{ "nct",   0,  39600, "Pacific/Noumea"                },
	{ "nddt",  1,  -5400, "America/St_Johns"              },
	{ "nddt",  1,  -5400, "Canada/Newfoundland"           },
	{ "ndt",   1,  -9052, "America/St_Johns"              },
	{ "ndt",   1,  -9000, "America/St_Johns"              },
	{ "ndt",   1, -36000, "Pacific/Midway"                },
	{ "ndt",   1,  -9000, "America/Goose_Bay"             },
	{ "ndt",   1,  -9000, "Canada/Newfoundland"           },
	{ "ndt",   1,  -9052, "America/Goose_Bay"             },
	{ "ndt",   1,  -9052, "Canada/Newfoundland"           },
	{ "negt",  0, -12600, "America/Paramaribo"            },
	{ "nest",  1,   4800, "Europe/Amsterdam"              },
	{ "net",   0,   1200, "Europe/Amsterdam"              },
	{ "nft",   0,  41400, "Pacific/Norfolk"               },
	{ "nmt",   0,  20928, "Asia/Novokuznetsk"             },
	{ "nmt",   0,  40320, "Pacific/Norfolk"               },
	{ "novst", 1,  25200, "Asia/Novosibirsk"              },
	{ "novst", 1,  28800, "Asia/Novosibirsk"              },
	{ "novst", 1,  25200, "Asia/Novokuznetsk"             },
	{ "novt",  0,  21600, "Asia/Novosibirsk"              },
	{ "novt",  0,  25200, "Asia/Novosibirsk"              },
	{ "novt",  0,  21600, "Asia/Novokuznetsk"             },
	{ "novt",  0,  25200, "Asia/Novokuznetsk"             },
	{ "npt",   1,  -9000, "America/St_Johns"              },
	{ "npt",   0,  20700, "Asia/Katmandu"                 },
	{ "npt",   1, -36000, "America/Adak"                  },
	{ "npt",   1, -36000, "America/Atka"                  },
	{ "npt",   1, -36000, "America/Nome"                  },
	{ "npt",   1,  -9000, "America/Goose_Bay"             },
	{ "npt",   1,  -9000, "Canada/Newfoundland"           },
	{ "npt",   0,  20700, "Asia/Kathmandu"                },
	{ "nrt",   0,  43200, "Pacific/Nauru"                 },
	{ "nrt",   0,  41400, "Pacific/Nauru"                 },
	{ "nst",   0, -12600, "America/St_Johns"              },
	{ "nst",   0, -12652, "America/St_Johns"              },
	{ "nst",   1,   4772, "Europe/Amsterdam"              },
	{ "nst",   0, -12600, "America/Goose_Bay"             },
	{ "nst",   0, -12600, "Canada/Newfoundland"           },
	{ "nst",   0, -12652, "America/Goose_Bay"             },
	{ "nst",   0, -12652, "Canada/Newfoundland"           },
	{ "nst",   0, -39600, "America/Adak"                  },
	{ "nst",   0, -39600, "America/Atka"                  },
	{ "nst",   0, -39600, "America/Nome"                  },
	{ "nst",   0, -39600, "Pacific/Midway"                },
	{ "nst",   0, -39600, "Pacific/Pago_Pago"             },
	{ "nst",   0, -39600, "Pacific/Samoa"                 },
	{ "nut",   0, -39600, "Pacific/Niue"                  },
	{ "nut",   0, -40800, "Pacific/Niue"                  },
	{ "nut",   0, -41400, "Pacific/Niue"                  },
	{ "nwt",   1,  -9000, "America/St_Johns"              },
	{ "nwt",   1, -36000, "America/Adak"                  },
	{ "nwt",   1, -36000, "America/Atka"                  },
	{ "nwt",   1, -36000, "America/Nome"                  },
	{ "nwt",   1,  -9000, "America/Goose_Bay"             },
	{ "nwt",   1,  -9000, "Canada/Newfoundland"           },
	{ "nzdt",  1,  46800, "Pacific/Auckland"              },
	{ "nzdt",  1,  46800, "Antarctica/McMurdo"            },
	{ "nzdt",  1,  46800, "Antarctica/South_Pole"         },
	{ "nzdt",  1,  46800, "NZ"                            },
	{ "nzmt",  0,  41400, "Pacific/Auckland"              },
	{ "nzmt",  0,  41400, "Antarctica/McMurdo"            },
	{ "nzmt",  0,  41400, "Antarctica/South_Pole"         },
	{ "nzmt",  0,  41400, "NZ"                            },
	{ "nzst",  0,  43200, "Pacific/Auckland"              },
	{ "nzst",  1,  43200, "Pacific/Auckland"              },
	{ "nzst",  1,  45000, "Pacific/Auckland"              },
	{ "nzst",  0,  43200, "Antarctica/McMurdo"            },
	{ "nzst",  0,  43200, "Antarctica/South_Pole"         },
	{ "nzst",  0,  43200, "NZ"                            },
	{ "nzst",  1,  43200, "Antarctica/McMurdo"            },
	{ "nzst",  1,  43200, "Antarctica/South_Pole"         },
	{ "nzst",  1,  43200, "NZ"                            },
	{ "nzst",  1,  45000, "Antarctica/McMurdo"            },
	{ "nzst",  1,  45000, "Antarctica/South_Pole"         },
	{ "nzst",  1,  45000, "NZ"                            },
	{ "omsst", 1,  25200, "Asia/Omsk"                     },
	{ "omsst", 1,  21600, "Asia/Omsk"                     },
	{ "omst",  0,  21600, "Asia/Omsk"                     },
	{ "omst",  0,  18000, "Asia/Omsk"                     },
	{ "omst",  0,  25200, "Asia/Omsk"                     },
	{ "orast", 1,  18000, "Asia/Oral"                     },
	{ "orat",  0,  18000, "Asia/Oral"                     },
	{ "orat",  0,  14400, "Asia/Oral"                     },
	{ "pddt",  1, -21600, "America/Inuvik"                },
	{ "pdt",   1, -25200, "America/Los_Angeles"           },
	{ "pdt",   1, -25200, "America/Boise"                 },
	{ "pdt",   1, -25200, "America/Dawson"                },
	{ "pdt",   1, -25200, "America/Dawson_Creek"          },
	{ "pdt",   1, -25200, "America/Ensenada"              },
	{ "pdt",   1, -25200, "America/Juneau"                },
	{ "pdt",   1, -25200, "America/Metlakatla"            },
	{ "pdt",   1, -25200, "America/Santa_Isabel"          },
	{ "pdt",   1, -25200, "America/Sitka"                 },
	{ "pdt",   1, -25200, "America/Tijuana"               },
	{ "pdt",   1, -25200, "America/Vancouver"             },
	{ "pdt",   1, -25200, "America/Whitehorse"            },
	{ "pdt",   1, -25200, "Canada/Pacific"                },
	{ "pdt",   1, -25200, "Canada/Yukon"                  },
	{ "pdt",   1, -25200, "Mexico/BajaNorte"              },
	{ "pest",  1, -14400, "America/Lima"                  },
	{ "petst", 1,  46800, "Asia/Kamchatka"                },
	{ "petst", 1,  43200, "Asia/Kamchatka"                },
	{ "pett",  0,  43200, "Asia/Kamchatka"                },
	{ "pett",  0,  39600, "Asia/Kamchatka"                },
	{ "pet",   0, -18000, "America/Lima"                  },
	{ "pgt",   0,  36000, "Pacific/Port_Moresby"          },
	{ "phot",  0,  46800, "Pacific/Enderbury"             },
	{ "phot",  0, -39600, "Pacific/Enderbury"             },
	{ "phot",  0, -43200, "Pacific/Enderbury"             },
	{ "phst",  1,  32400, "Asia/Manila"                   },
	{ "pht",   0,  28800, "Asia/Manila"                   },
	{ "pkst",  1,  21600, "Asia/Karachi"                  },
	{ "pkt",   0,  18000, "Asia/Karachi"                  },
	{ "pmdt",  1,  -7200, "America/Miquelon"              },
	{ "pmst",  0, -10800, "America/Miquelon"              },
	{ "pmt",   0, -13236, "America/Paramaribo"            },
	{ "pmt",   0, -13252, "America/Paramaribo"            },
	{ "pmt",   0,  36000, "Antarctica/DumontDUrville"     },
	{ "pmt",   0,  26240, "Asia/Pontianak"                },
	{ "pmt",   0,    561, "Africa/Algiers"                },
	{ "pmt",   0,    561, "Africa/Tunis"                  },
	{ "pmt",   0,    561, "Europe/Monaco"                 },
	{ "pmt",   0,    561, "Europe/Paris"                  },
	{ "pmt",   0,    561, "WET"                           },
	{ "pnt",   0, -30600, "Pacific/Pitcairn"              },
	{ "pont",  0,  39600, "Pacific/Pohnpei"               },
	{ "pont",  0,  39600, "Pacific/Ponape"                },
	{ "ppmt",  0, -17340, "America/Port-au-Prince"        },
	{ "ppt",   1, -25200, "America/Los_Angeles"           },
	{ "ppt",   1, -25200, "America/Dawson_Creek"          },
	{ "ppt",   1, -25200, "America/Ensenada"              },
	{ "ppt",   1, -25200, "America/Juneau"                },
	{ "ppt",   1, -25200, "America/Metlakatla"            },
	{ "ppt",   1, -25200, "America/Santa_Isabel"          },
	{ "ppt",   1, -25200, "America/Sitka"                 },
	{ "ppt",   1, -25200, "America/Tijuana"               },
	{ "ppt",   1, -25200, "America/Vancouver"             },
	{ "ppt",   1, -25200, "Canada/Pacific"                },
	{ "ppt",   1, -25200, "Mexico/BajaNorte"              },
	{ "pst",   0, -28800, "America/Los_Angeles"           },
	{ "pst",   0, -28800, "America/Bahia_Banderas"        },
	{ "pst",   0, -28800, "America/Boise"                 },
	{ "pst",   0, -28800, "America/Creston"               },
	{ "pst",   0, -28800, "America/Dawson"                },
	{ "pst",   0, -28800, "America/Dawson_Creek"          },
	{ "pst",   0, -28800, "America/Ensenada"              },
	{ "pst",   0, -28800, "America/Hermosillo"            },
	{ "pst",   0, -28800, "America/Inuvik"                },
	{ "pst",   0, -28800, "America/Juneau"                },
	{ "pst",   0, -28800, "America/Mazatlan"              },
	{ "pst",   0, -28800, "America/Metlakatla"            },
	{ "pst",   0, -28800, "America/Santa_Isabel"          },
	{ "pst",   0, -28800, "America/Sitka"                 },
	{ "pst",   0, -28800, "America/Tijuana"               },
	{ "pst",   0, -28800, "America/Vancouver"             },
	{ "pst",   0, -28800, "America/Whitehorse"            },
	{ "pst",   0, -28800, "Canada/Pacific"                },
	{ "pst",   0, -28800, "Canada/Yukon"                  },
	{ "pst",   0, -28800, "Mexico/BajaNorte"              },
	{ "pst",   0, -28800, "Mexico/BajaSur"                },
	{ "pst",   0, -28800, "Pacific/Pitcairn"              },
	{ "pwt",   1, -25200, "America/Los_Angeles"           },
	{ "pwt",   1, -25200, "America/Dawson_Creek"          },
	{ "pwt",   1, -25200, "America/Ensenada"              },
	{ "pwt",   1, -25200, "America/Juneau"                },
	{ "pwt",   1, -25200, "America/Metlakatla"            },
	{ "pwt",   1, -25200, "America/Santa_Isabel"          },
	{ "pwt",   1, -25200, "America/Sitka"                 },
	{ "pwt",   1, -25200, "America/Tijuana"               },
	{ "pwt",   1, -25200, "America/Vancouver"             },
	{ "pwt",   1, -25200, "Canada/Pacific"                },
	{ "pwt",   1, -25200, "Mexico/BajaNorte"              },
	{ "pwt",   0,  32400, "Pacific/Palau"                 },
	{ "pyst",  1, -10800, "America/Asuncion"              },
	{ "pyt",   0, -14400, "America/Asuncion"              },
	{ "pyt",   0, -10800, "America/Asuncion"              },
	{ "qmt",   0, -18840, "America/Guayaquil"             },
	{ "qyzst", 1,  25200, "Asia/Qyzylorda"                },
	{ "qyzt",  0,  21600, "Asia/Qyzylorda"                },
	{ "qyzt",  0,  18000, "Asia/Qyzylorda"                },
	{ "ret",   0,  14400, "Indian/Reunion"                },
	{ "rmt",   0,   5788, "Europe/Riga"                   },
	{ "rmt",   0,  -5268, "Atlantic/Reykjavik"            },
	{ "rmt",   0,  23080, "Asia/Rangoon"                  },
	{ "rott",  0, -10800, "Antarctica/Rothera"            },
	{ "sakst", 1,  39600, "Asia/Sakhalin"                 },
	{ "sakst", 1,  43200, "Asia/Sakhalin"                 },
	{ "sakt",  0,  36000, "Asia/Sakhalin"                 },
	{ "sakt",  0,  39600, "Asia/Sakhalin"                 },
	{ "samst", 1,  21600, "Asia/Samarkand"                },
	{ "samst", 1,  14400, "Europe/Samara"                 },
	{ "samst", 1,  18000, "Europe/Samara"                 },
	{ "samt",  0,  18000, "Asia/Samarkand"                },
	{ "samt",  0,  14400, "Asia/Samarkand"                },
	{ "samt",  0,  10800, "Europe/Samara"                 },
	{ "samt",  0,  14400, "Europe/Samara"                 },
	{ "sast",  0,   7200, "Africa/Johannesburg"           },
	{ "sast",  1,  10800, "Africa/Johannesburg"           },
	{ "sast",  0,   5400, "Africa/Johannesburg"           },
	{ "sast",  1,  10800, "Africa/Maseru"                 },
	{ "sast",  1,  10800, "Africa/Windhoek"               },
	{ "sast",  0,   5400, "Africa/Gaborone"               },
	{ "sast",  0,   7200, "Africa/Maseru"                 },
	{ "sast",  0,   7200, "Africa/Mbabane"                },
	{ "sast",  0,   7200, "Africa/Windhoek"               },
	{ "sbt",   0,  39600, "Pacific/Guadalcanal"           },
	{ "sct",   0,  14400, "Indian/Mahe"                   },
	{ "sdmt",  0, -16800, "America/Santo_Domingo"         },
	{ "sdt",   1, -36000, "Pacific/Apia"                  },
	{ "sgt",   0,  28800, "Asia/Singapore"                },
	{ "sgt",   0,  27000, "Asia/Singapore"                },
	{ "shest", 1,  21600, "Asia/Aqtau"                    },
	{ "shet",  0,  21600, "Asia/Aqtau"                    },
	{ "shet",  0,  18000, "Asia/Aqtau"                    },
	{ "sjmt",  0, -20173, "America/Costa_Rica"            },
	{ "smt",   0,  25580, "Asia/Saigon"                   },
	{ "smt",   0, -13884, "Atlantic/Stanley"              },
	{ "smt",   0, -16966, "America/Santiago"              },
	{ "smt",   0, -16966, "Chile/Continental"             },
	{ "smt",   0,  24925, "Asia/Kuala_Lumpur"             },
	{ "smt",   0,  24925, "Asia/Singapore"                },
	{ "smt",   0,  25580, "Asia/Ho_Chi_Minh"              },
	{ "smt",   0,  25580, "Asia/Phnom_Penh"               },
	{ "smt",   0,  25580, "Asia/Vientiane"                },
	{ "smt",   0,   8160, "Europe/Simferopol"             },
	{ "sret",  0,  39600, "Asia/Srednekolymsk"            },
	{ "srt",   0, -10800, "America/Paramaribo"            },
	{ "srt",   0, -12600, "America/Paramaribo"            },
	{ "sst",   0, -39600, "Pacific/Samoa"                 },
	{ "sst",   0, -39600, "Pacific/Apia"                  },
	{ "sst",   0, -39600, "Pacific/Midway"                },
	{ "sst",   0, -39600, "Pacific/Pago_Pago"             },
	{ "stat",  0,  10800, "Europe/Volgograd"              },
	{ "stat",  0,  14400, "Europe/Volgograd"              },
	{ "svest", 1,  21600, "Asia/Yekaterinburg"            },
	{ "svest", 1,  18000, "Asia/Yekaterinburg"            },
	{ "svet",  0,  18000, "Asia/Yekaterinburg"            },
	{ "svet",  0,  14400, "Asia/Yekaterinburg"            },
	{ "swat",  0,   5400, "Africa/Windhoek"               },
	{ "syot",  0,  10800, "Antarctica/Syowa"              },
	{ "taht",  0, -36000, "Pacific/Tahiti"                },
	{ "tasst", 1,  25200, "Asia/Samarkand"                },
	{ "tasst", 1,  21600, "Asia/Tashkent"                 },
	{ "tasst", 1,  25200, "Asia/Tashkent"                 },
	{ "tast",  0,  21600, "Asia/Samarkand"                },
	{ "tast",  0,  18000, "Asia/Tashkent"                 },
	{ "tast",  0,  21600, "Asia/Tashkent"                 },
	{ "tbist", 1,  18000, "Asia/Tbilisi"                  },
	{ "tbist", 1,  14400, "Asia/Tbilisi"                  },
	{ "tbit",  0,  14400, "Asia/Tbilisi"                  },
	{ "tbit",  0,  10800, "Asia/Tbilisi"                  },
	{ "tbmt",  0,  10746, "Asia/Tbilisi"                  },
	{ "tft",   0,  18000, "Indian/Kerguelen"              },
	{ "tjt",   0,  18000, "Asia/Dushanbe"                 },
	{ "tkt",   0, -39600, "Pacific/Fakaofo"               },
	{ "tkt",   0,  46800, "Pacific/Fakaofo"               },
	{ "tlt",   0,  32400, "Asia/Dili"                     },
	{ "tlt",   0,  28800, "Asia/Dili"                     },
	{ "tmt",   0,  12344, "Asia/Tehran"                   },
	{ "tmt",   0,   5940, "Europe/Tallinn"                },
	{ "tmt",   0,  14400, "Asia/Ashgabat"                 },
	{ "tmt",   0,  14400, "Asia/Ashkhabad"                },
	{ "tmt",   0,  18000, "Asia/Ashgabat"                 },
	{ "tmt",   0,  18000, "Asia/Ashkhabad"                },
	{ "tost",  1,  50400, "Pacific/Tongatapu"             },
	{ "tot",   0,  46800, "Pacific/Tongatapu"             },
	{ "tot",   0,  44400, "Pacific/Tongatapu"             },
	{ "trst",  1,  14400, "Europe/Istanbul"               },
	{ "trst",  1,  14400, "Asia/Istanbul"                 },
	{ "trt",   0,  10800, "Europe/Istanbul"               },
	{ "trt",   0,  10800, "Asia/Istanbul"                 },
	{ "tsat",  0,  10800, "Europe/Volgograd"              },
	{ "tvt",   0,  43200, "Pacific/Funafuti"              },
	{ "uct",   0,      0, "Etc/UCT"                       },
	{ "uct",   0,      0, "UCT"                           },
	{ "ulast", 1,  32400, "Asia/Ulaanbaatar"              },
	{ "ulast", 1,  32400, "Asia/Ulan_Bator"               },
	{ "ulat",  0,  28800, "Asia/Ulaanbaatar"              },
	{ "ulat",  0,  25200, "Asia/Ulaanbaatar"              },
	{ "ulat",  0,  25200, "Asia/Choibalsan"               },
	{ "ulat",  0,  25200, "Asia/Ulan_Bator"               },
	{ "ulat",  0,  28800, "Asia/Choibalsan"               },
	{ "ulat",  0,  28800, "Asia/Ulan_Bator"               },
	{ "urast", 1,  21600, "Asia/Oral"                     },
	{ "urast", 1,  18000, "Asia/Oral"                     },
	{ "urat",  0,  21600, "Asia/Oral"                     },
	{ "urat",  0,  14400, "Asia/Oral"                     },
	{ "urat",  0,  18000, "Asia/Oral"                     },
	{ "utc",   0,      0, "Antarctica/Troll"              },
	{ "utc",   0,      0, "Etc/Universal"                 },
	{ "utc",   0,      0, "Etc/UTC"                       },
	{ "utc",   0,      0, "Etc/Zulu"                      },
	{ "utc",   0,      0, "GMT"                           },
	{ "utc",   0,      0, "UTC"                           },
	{ "utc",   0,      0, "UTC"                           },
	{ "uyhst", 1,  -9000, "America/Montevideo"            },
	{ "uyhst", 1, -10800, "America/Montevideo"            },
	{ "uyst",  1,  -7200, "America/Montevideo"            },
	{ "uyt",   0, -10800, "America/Montevideo"            },
	{ "uyt",   0, -12600, "America/Montevideo"            },
	{ "uzst",  1,  21600, "Asia/Samarkand"                },
	{ "uzst",  1,  21600, "Asia/Tashkent"                 },
	{ "uzt",   0,  18000, "Asia/Samarkand"                },
	{ "uzt",   0,  18000, "Asia/Tashkent"                 },
	{ "vet",   0, -16200, "America/Caracas"               },
	{ "vet",   0, -14400, "America/Caracas"               },
	{ "vlast", 1,  39600, "Asia/Vladivostok"              },
	{ "vlast", 1,  36000, "Asia/Vladivostok"              },
	{ "vlast", 1,  39600, "Asia/Khandyga"                 },
	{ "vlat",  0,  36000, "Asia/Vladivostok"              },
	{ "vlat",  0,  32400, "Asia/Vladivostok"              },
	{ "vlat",  0,  39600, "Asia/Vladivostok"              },
	{ "vlat",  0,  36000, "Asia/Khandyga"                 },
	{ "vlat",  0,  36000, "Asia/Ust-Nera"                 },
	{ "vlat",  0,  39600, "Asia/Khandyga"                 },
	{ "vlat",  0,  39600, "Asia/Ust-Nera"                 },
	{ "volst", 1,  14400, "Europe/Volgograd"              },
	{ "volst", 1,  18000, "Europe/Volgograd"              },
	{ "volt",  0,  10800, "Europe/Volgograd"              },
	{ "volt",  0,  14400, "Europe/Volgograd"              },
	{ "vost",  0,  21600, "Antarctica/Vostok"             },
	{ "vust",  1,  43200, "Pacific/Efate"                 },
	{ "vut",   0,  39600, "Pacific/Efate"                 },
	{ "wakt",  0,  43200, "Pacific/Wake"                  },
	{ "warst", 1, -10800, "America/Mendoza"               },
	{ "warst", 1, -10800, "America/Argentina/Jujuy"       },
	{ "warst", 1, -10800, "America/Argentina/Mendoza"     },
	{ "warst", 1, -10800, "America/Argentina/San_Luis"    },
	{ "warst", 1, -10800, "America/Jujuy"                 },
	{ "wart",  0, -14400, "America/Mendoza"               },
	{ "wart",  0, -14400, "America/Argentina/Catamarca"   },
	{ "wart",  0, -14400, "America/Argentina/ComodRivadavia" },
	{ "wart",  0, -14400, "America/Argentina/Cordoba"     },
	{ "wart",  0, -14400, "America/Argentina/Jujuy"       },
	{ "wart",  0, -14400, "America/Argentina/La_Rioja"    },
	{ "wart",  0, -14400, "America/Argentina/Mendoza"     },
	{ "wart",  0, -14400, "America/Argentina/Rio_Gallegos" },
	{ "wart",  0, -14400, "America/Argentina/Salta"       },
	{ "wart",  0, -14400, "America/Argentina/San_Juan"    },
	{ "wart",  0, -14400, "America/Argentina/San_Luis"    },
	{ "wart",  0, -14400, "America/Argentina/Tucuman"     },
	{ "wart",  0, -14400, "America/Argentina/Ushuaia"     },
	{ "wart",  0, -14400, "America/Catamarca"             },
	{ "wart",  0, -14400, "America/Cordoba"               },
	{ "wart",  0, -14400, "America/Jujuy"                 },
	{ "wart",  0, -14400, "America/Rosario"               },
	{ "wast",  1,   7200, "Africa/Windhoek"               },
	{ "wast",  1,   7200, "Africa/Ndjamena"               },
	{ "wat",   0,   3600, "Africa/Brazzaville"            },
	{ "wat",   0,  -3600, "Africa/Bissau"                 },
	{ "wat",   0,  -3600, "Africa/El_Aaiun"               },
	{ "wat",   0,  -3600, "Africa/Niamey"                 },
	{ "wat",   0,   3600, "Africa/Bangui"                 },
	{ "wat",   0,   3600, "Africa/Douala"                 },
	{ "wat",   0,   3600, "Africa/Kinshasa"               },
	{ "wat",   0,   3600, "Africa/Lagos"                  },
	{ "wat",   0,   3600, "Africa/Libreville"             },
	{ "wat",   0,   3600, "Africa/Luanda"                 },
	{ "wat",   0,   3600, "Africa/Malabo"                 },
	{ "wat",   0,   3600, "Africa/Ndjamena"               },
	{ "wat",   0,   3600, "Africa/Niamey"                 },
	{ "wat",   0,   3600, "Africa/Porto-Novo"             },
	{ "wat",   0,   3600, "Africa/Windhoek"               },
	{ "wemt",  1,   7200, "Europe/Lisbon"                 },
	{ "wemt",  1,   7200, "Europe/Madrid"                 },
	{ "wemt",  1,   7200, "Europe/Monaco"                 },
	{ "wemt",  1,   7200, "Europe/Paris"                  },
	{ "wemt",  1,   7200, "WET"                           },
	{ "west",  1,   3600, "Europe/Paris"                  },
	{ "west",  1,   7200, "Europe/Luxembourg"             },
	{ "west",  1,   3600, "Africa/Algiers"                },
	{ "west",  1,   3600, "Africa/Casablanca"             },
	{ "west",  1,   3600, "Africa/Ceuta"                  },
	{ "west",  1,   3600, "Africa/El_Aaiun"               },
	{ "west",  1,   3600, "Atlantic/Canary"               },
	{ "west",  1,   3600, "Atlantic/Faeroe"               },
	{ "west",  1,   3600, "Atlantic/Faroe"                },
	{ "west",  1,   3600, "Atlantic/Madeira"              },
	{ "west",  1,   3600, "Europe/Brussels"               },
	{ "west",  1,   3600, "Europe/Lisbon"                 },
	{ "west",  1,   3600, "Europe/Luxembourg"             },
	{ "west",  1,   3600, "Europe/Madrid"                 },
	{ "west",  1,   3600, "Europe/Monaco"                 },
	{ "west",  1,   3600, "WET"                           },
	{ "wet",   0,      0, "Europe/Paris"                  },
	{ "wet",   0,   3600, "Europe/Luxembourg"             },
	{ "wet",   0,      0, "Africa/Algiers"                },
	{ "wet",   0,      0, "Africa/Casablanca"             },
	{ "wet",   0,      0, "Africa/Ceuta"                  },
	{ "wet",   0,      0, "Africa/El_Aaiun"               },
	{ "wet",   0,      0, "Atlantic/Azores"               },
	{ "wet",   0,      0, "Atlantic/Canary"               },
	{ "wet",   0,      0, "Atlantic/Faeroe"               },
	{ "wet",   0,      0, "Atlantic/Faroe"                },
	{ "wet",   0,      0, "Atlantic/Madeira"              },
	{ "wet",   0,      0, "Europe/Andorra"                },
	{ "wet",   0,      0, "Europe/Brussels"               },
	{ "wet",   0,      0, "Europe/Lisbon"                 },
	{ "wet",   0,      0, "Europe/Luxembourg"             },
	{ "wet",   0,      0, "Europe/Madrid"                 },
	{ "wet",   0,      0, "Europe/Monaco"                 },
	{ "wet",   0,      0, "WET"                           },
	{ "wft",   0,  43200, "Pacific/Wallis"                },
	{ "wgst",  1,  -7200, "America/Godthab"               },
	{ "wgst",  1,  -7200, "America/Danmarkshavn"          },
	{ "wgt",   0, -10800, "America/Godthab"               },
	{ "wgt",   0, -10800, "America/Danmarkshavn"          },
	{ "wib",   0,  25200, "Asia/Jakarta"                  },
	{ "wib",   0,  25200, "Asia/Pontianak"                },
	{ "wib",   0,  27000, "Asia/Jakarta"                  },
	{ "wib",   0,  27000, "Asia/Pontianak"                },
	{ "wib",   0,  28800, "Asia/Jakarta"                  },
	{ "wib",   0,  28800, "Asia/Pontianak"                },
	{ "wita",  0,  28800, "Asia/Dili"                     },
	{ "wita",  0,  28800, "Asia/Makassar"                 },
	{ "wita",  0,  28800, "Asia/Pontianak"                },
	{ "wita",  0,  28800, "Asia/Ujung_Pandang"            },
	{ "wit",   0,  32400, "Asia/Jayapura"                 },
	{ "wmt",   0,   5040, "Europe/Vilnius"                },
	{ "wmt",   0,   5040, "Europe/Warsaw"                 },
	{ "wsdt",  1,  50400, "Pacific/Apia"                  },
	{ "wsst",  0, -41400, "Pacific/Apia"                  },
	{ "wsst",  0,  46800, "Pacific/Apia"                  },
	{ "xjt",   0,  21600, "Asia/Kashgar"                  },
	{ "xjt",   0,  21600, "Asia/Urumqi"                   },
	{ "yakst", 1,  36000, "Asia/Yakutsk"                  },
	{ "yakst", 1,  32400, "Asia/Yakutsk"                  },
	{ "yakst", 1,  32400, "Asia/Chita"                    },
	{ "yakst", 1,  32400, "Asia/Khandyga"                 },
	{ "yakst", 1,  36000, "Asia/Chita"                    },
	{ "yakst", 1,  36000, "Asia/Khandyga"                 },
	{ "yakt",  0,  32400, "Asia/Yakutsk"                  },
	{ "yakt",  0,  28800, "Asia/Yakutsk"                  },
	{ "yakt",  0,  36000, "Asia/Yakutsk"                  },
	{ "yakt",  0,  28800, "Asia/Chita"                    },
	{ "yakt",  0,  28800, "Asia/Khandyga"                 },
	{ "yakt",  0,  28800, "Asia/Ust-Nera"                 },
	{ "yakt",  0,  32400, "Asia/Chita"                    },
	{ "yakt",  0,  32400, "Asia/Khandyga"                 },
	{ "yakt",  0,  32400, "Asia/Ust-Nera"                 },
	{ "yakt",  0,  36000, "Asia/Chita"                    },
	{ "yakt",  0,  36000, "Asia/Khandyga"                 },
	{ "yddt",  1, -25200, "America/Dawson"                },
	{ "yddt",  1, -25200, "America/Whitehorse"            },
	{ "yddt",  1, -25200, "Canada/Yukon"                  },
	{ "ydt",   1, -28800, "America/Dawson"                },
	{ "ydt",   1, -28800, "America/Juneau"                },
	{ "ydt",   1, -28800, "America/Whitehorse"            },
	{ "ydt",   1, -28800, "America/Yakutat"               },
	{ "ydt",   1, -28800, "Canada/Yukon"                  },
	{ "yekst", 1,  21600, "Asia/Yekaterinburg"            },
	{ "yekt",  0,  18000, "Asia/Yekaterinburg"            },
	{ "yekt",  0,  21600, "Asia/Yekaterinburg"            },
	{ "yerst", 1,  18000, "Asia/Yerevan"                  },
	{ "yerst", 1,  14400, "Asia/Yerevan"                  },
	{ "yert",  0,  14400, "Asia/Yerevan"                  },
	{ "yert",  0,  10800, "Asia/Yerevan"                  },
	{ "ypt",   1, -28800, "America/Dawson"                },
	{ "ypt",   1, -28800, "America/Whitehorse"            },
	{ "ypt",   1, -28800, "America/Yakutat"               },
	{ "ypt",   1, -28800, "Canada/Yukon"                  },
	{ "yst",   0, -32400, "America/Anchorage"             },
	{ "yst",   0, -32400, "America/Dawson"                },
	{ "yst",   0, -32400, "America/Juneau"                },
	{ "yst",   0, -32400, "America/Nome"                  },
	{ "yst",   0, -32400, "America/Sitka"                 },
	{ "yst",   0, -32400, "America/Whitehorse"            },
	{ "yst",   0, -32400, "America/Yakutat"               },
	{ "yst",   0, -32400, "Canada/Yukon"                  },
	{ "ywt",   1, -28800, "America/Dawson"                },
	{ "ywt",   1, -28800, "America/Whitehorse"            },
	{ "ywt",   1, -28800, "America/Yakutat"               },
	{ "ywt",   1, -28800, "Canada/Yukon"                  },
	{ "a",     0,   3600, NULL                            },
	{ "b",     0,   7200, NULL                            },
	{ "c",     0,  10800, NULL                            },
	{ "d",     0,  14400, NULL                            },
	{ "e",     0,  18000, NULL                            },
	{ "f",     0,  21600, NULL                            },
	{ "g",     0,  25200, NULL                            },
	{ "h",     0,  28800, NULL                            },
	{ "i",     0,  32400, NULL                            },
	{ "k",     0,  36000, NULL                            },
	{ "l",     0,  39600, NULL                            },
	{ "m",     0,  43200, NULL                            },
	{ "n",     0,  -3600, NULL                            },
	{ "o",     0,  -7200, NULL                            },
	{ "p",     0, -10800, NULL                            },
	{ "q",     0, -14400, NULL                            },
	{ "r",     0, -18000, NULL                            },
	{ "s",     0, -21600, NULL                            },
	{ "t",     0, -25200, NULL                            },
	{ "u",     0, -28800, NULL                            },
	{ "v",     0, -32400, NULL                            },
	{ "w",     0, -36000, NULL                            },
	{ "x",     0, -39600, NULL                            },
	{ "y",     0, -43200, NULL                            },
	{ "zzz",   0,      0, "Antarctica/Davis"              },
	{ "zzz",   0,      0, "America/Cambridge_Bay"         },
	{ "zzz",   0,      0, "America/Inuvik"                },
	{ "zzz",   0,      0, "America/Iqaluit"               },
	{ "zzz",   0,      0, "America/Pangnirtung"           },
	{ "zzz",   0,      0, "America/Rankin_Inlet"          },
	{ "zzz",   0,      0, "America/Resolute"              },
	{ "zzz",   0,      0, "America/Yellowknife"           },
	{ "zzz",   0,      0, "Antarctica/Casey"              },
	{ "zzz",   0,      0, "Antarctica/DumontDUrville"     },
	{ "zzz",   0,      0, "Antarctica/Macquarie"          },
	{ "zzz",   0,      0, "Antarctica/Mawson"             },
	{ "zzz",   0,      0, "Antarctica/Palmer"             },
	{ "zzz",   0,      0, "Antarctica/Rothera"            },
	{ "zzz",   0,      0, "Antarctica/Syowa"              },
	{ "zzz",   0,      0, "Antarctica/Troll"              },
	{ "zzz",   0,      0, "Antarctica/Vostok"             },
	{ "zzz",   0,      0, "Indian/Kerguelen"              },
	{ "z",     0,      0, NULL                            },


<|start_filename|>ext/pcre/pcrelib/sljit/sljitNativeMIPS_64.c<|end_filename|>
/*
 *    Stack-less Just-In-Time compiler
 *
 *    Copyright 2009-2012 <NAME> (<EMAIL>). All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification, are
 * permitted provided that the following conditions are met:
 *
 *   1. Redistributions of source code must retain the above copyright notice, this list of
 *      conditions and the following disclaimer.
 *
 *   2. Redistributions in binary form must reproduce the above copyright notice, this list
 *      of conditions and the following disclaimer in the documentation and/or other materials
 *      provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) AND CONTRIBUTORS ``AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
 * SHALL THE COPYRIGHT HOLDER(S) OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
 * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

/* mips 64-bit arch dependent functions. */

static sljit_si load_immediate(struct sljit_compiler *compiler, sljit_si dst_ar, sljit_sw imm)
{
	sljit_si shift = 32;
	sljit_si shift2;
	sljit_si inv = 0;
	sljit_ins ins;
	sljit_uw uimm;

	if (!(imm & ~0xffff))
		return push_inst(compiler, ORI | SA(0) | TA(dst_ar) | IMM(imm), dst_ar);

	if (imm < 0 && imm >= SIMM_MIN)
		return push_inst(compiler, ADDIU | SA(0) | TA(dst_ar) | IMM(imm), dst_ar);

	if (imm <= 0x7fffffffl && imm >= -0x80000000l) {
		FAIL_IF(push_inst(compiler, LUI | TA(dst_ar) | IMM(imm >> 16), dst_ar));
		return (imm & 0xffff) ? push_inst(compiler, ORI | SA(dst_ar) | TA(dst_ar) | IMM(imm), dst_ar) : SLJIT_SUCCESS;
	}

	/* Zero extended number. */
	uimm = imm;
	if (imm < 0) {
		uimm = ~imm;
		inv = 1;
	}

	while (!(uimm & 0xff00000000000000l)) {
		shift -= 8;
		uimm <<= 8;
	}

	if (!(uimm & 0xf000000000000000l)) {
		shift -= 4;
		uimm <<= 4;
	}

	if (!(uimm & 0xc000000000000000l)) {
		shift -= 2;
		uimm <<= 2;
	}

	if ((sljit_sw)uimm < 0) {
		uimm >>= 1;
		shift += 1;
	}
	SLJIT_ASSERT(((uimm & 0xc000000000000000l) == 0x4000000000000000l) && (shift > 0) && (shift <= 32));

	if (inv)
		uimm = ~uimm;

	FAIL_IF(push_inst(compiler, LUI | TA(dst_ar) | IMM(uimm >> 48), dst_ar));
	if (uimm & 0x0000ffff00000000l)
		FAIL_IF(push_inst(compiler, ORI | SA(dst_ar) | TA(dst_ar) | IMM(uimm >> 32), dst_ar));

	imm &= (1l << shift) - 1;
	if (!(imm & ~0xffff)) {
		ins = (shift == 32) ? DSLL32 : DSLL;
		if (shift < 32)
			ins |= SH_IMM(shift);
		FAIL_IF(push_inst(compiler, ins | TA(dst_ar) | DA(dst_ar), dst_ar));
		return !(imm & 0xffff) ? SLJIT_SUCCESS : push_inst(compiler, ORI | SA(dst_ar) | TA(dst_ar) | IMM(imm), dst_ar);
	}

	/* Double shifts needs to be performed. */
	uimm <<= 32;
	shift2 = shift - 16;

	while (!(uimm & 0xf000000000000000l)) {
		shift2 -= 4;
		uimm <<= 4;
	}

	if (!(uimm & 0xc000000000000000l)) {
		shift2 -= 2;
		uimm <<= 2;
	}

	if (!(uimm & 0x8000000000000000l)) {
		shift2--;
		uimm <<= 1;
	}

	SLJIT_ASSERT((uimm & 0x8000000000000000l) && (shift2 > 0) && (shift2 <= 16));

	FAIL_IF(push_inst(compiler, DSLL | TA(dst_ar) | DA(dst_ar) | SH_IMM(shift - shift2), dst_ar));
	FAIL_IF(push_inst(compiler, ORI | SA(dst_ar) | TA(dst_ar) | IMM(uimm >> 48), dst_ar));
	FAIL_IF(push_inst(compiler, DSLL | TA(dst_ar) | DA(dst_ar) | SH_IMM(shift2), dst_ar));

	imm &= (1l << shift2) - 1;
	return !(imm & 0xffff) ? SLJIT_SUCCESS : push_inst(compiler, ORI | SA(dst_ar) | TA(dst_ar) | IMM(imm), dst_ar);
}

#define SELECT_OP(a, b) \
	(!(op & SLJIT_INT_OP) ? a : b)

#define EMIT_LOGICAL(op_imm, op_norm) \
	if (flags & SRC2_IMM) { \
		if (op & SLJIT_SET_E) \
			FAIL_IF(push_inst(compiler, op_imm | S(src1) | TA(EQUAL_FLAG) | IMM(src2), EQUAL_FLAG)); \
		if (CHECK_FLAGS(SLJIT_SET_E)) \
			FAIL_IF(push_inst(compiler, op_imm | S(src1) | T(dst) | IMM(src2), DR(dst))); \
	} \
	else { \
		if (op & SLJIT_SET_E) \
			FAIL_IF(push_inst(compiler, op_norm | S(src1) | T(src2) | DA(EQUAL_FLAG), EQUAL_FLAG)); \
		if (CHECK_FLAGS(SLJIT_SET_E)) \
			FAIL_IF(push_inst(compiler, op_norm | S(src1) | T(src2) | D(dst), DR(dst))); \
	}

#define EMIT_SHIFT(op_dimm, op_dimm32, op_imm, op_dv, op_v) \
	if (flags & SRC2_IMM) { \
		if (src2 >= 32) { \
			SLJIT_ASSERT(!(op & SLJIT_INT_OP)); \
			ins = op_dimm32; \
			src2 -= 32; \
		} \
		else \
			ins = (op & SLJIT_INT_OP) ? op_imm : op_dimm; \
		if (op & SLJIT_SET_E) \
			FAIL_IF(push_inst(compiler, ins | T(src1) | DA(EQUAL_FLAG) | SH_IMM(src2), EQUAL_FLAG)); \
		if (CHECK_FLAGS(SLJIT_SET_E)) \
			FAIL_IF(push_inst(compiler, ins | T(src1) | D(dst) | SH_IMM(src2), DR(dst))); \
	} \
	else { \
		ins = (op & SLJIT_INT_OP) ? op_v : op_dv; \
		if (op & SLJIT_SET_E) \
			FAIL_IF(push_inst(compiler, ins | S(src2) | T(src1) | DA(EQUAL_FLAG), EQUAL_FLAG)); \
		if (CHECK_FLAGS(SLJIT_SET_E)) \
			FAIL_IF(push_inst(compiler, ins | S(src2) | T(src1) | D(dst), DR(dst))); \
	}

static SLJIT_INLINE sljit_si emit_single_op(struct sljit_compiler *compiler, sljit_si op, sljit_si flags,
	sljit_si dst, sljit_si src1, sljit_sw src2)
{
	sljit_ins ins;

	switch (GET_OPCODE(op)) {
	case SLJIT_MOV:
	case SLJIT_MOV_P:
		SLJIT_ASSERT(src1 == TMP_REG1 && !(flags & SRC2_IMM));
		if (dst != src2)
			return push_inst(compiler, SELECT_OP(DADDU, ADDU) | S(src2) | TA(0) | D(dst), DR(dst));
		return SLJIT_SUCCESS;

	case SLJIT_MOV_UB:
	case SLJIT_MOV_SB:
		SLJIT_ASSERT(src1 == TMP_REG1 && !(flags & SRC2_IMM));
		if ((flags & (REG_DEST | REG2_SOURCE)) == (REG_DEST | REG2_SOURCE)) {
			if (op == SLJIT_MOV_SB) {
				FAIL_IF(push_inst(compiler, DSLL32 | T(src2) | D(dst) | SH_IMM(24), DR(dst)));
				return push_inst(compiler, DSRA32 | T(dst) | D(dst) | SH_IMM(24), DR(dst));
			}
			return push_inst(compiler, ANDI | S(src2) | T(dst) | IMM(0xff), DR(dst));
		}
		else if (dst != src2)
			SLJIT_ASSERT_STOP();
		return SLJIT_SUCCESS;

	case SLJIT_MOV_UH:
	case SLJIT_MOV_SH:
		SLJIT_ASSERT(src1 == TMP_REG1 && !(flags & SRC2_IMM));
		if ((flags & (REG_DEST | REG2_SOURCE)) == (REG_DEST | REG2_SOURCE)) {
			if (op == SLJIT_MOV_SH) {
				FAIL_IF(push_inst(compiler, DSLL32 | T(src2) | D(dst) | SH_IMM(16), DR(dst)));
				return push_inst(compiler, DSRA32 | T(dst) | D(dst) | SH_IMM(16), DR(dst));
			}
			return push_inst(compiler, ANDI | S(src2) | T(dst) | IMM(0xffff), DR(dst));
		}
		else if (dst != src2)
			SLJIT_ASSERT_STOP();
		return SLJIT_SUCCESS;

	case SLJIT_MOV_UI:
		SLJIT_ASSERT(!(op & SLJIT_INT_OP));
		FAIL_IF(push_inst(compiler, DSLL32 | T(src2) | D(dst) | SH_IMM(0), DR(dst)));
		return push_inst(compiler, DSRL32 | T(dst) | D(dst) | SH_IMM(0), DR(dst));

	case SLJIT_MOV_SI:
		SLJIT_ASSERT(src1 == TMP_REG1 && !(flags & SRC2_IMM));
		return push_inst(compiler, SLL | T(src2) | D(dst) | SH_IMM(0), DR(dst));

	case SLJIT_NOT:
		SLJIT_ASSERT(src1 == TMP_REG1 && !(flags & SRC2_IMM));
		if (op & SLJIT_SET_E)
			FAIL_IF(push_inst(compiler, NOR | S(src2) | T(src2) | DA(EQUAL_FLAG), EQUAL_FLAG));
		if (CHECK_FLAGS(SLJIT_SET_E))
			FAIL_IF(push_inst(compiler, NOR | S(src2) | T(src2) | D(dst), DR(dst)));
		return SLJIT_SUCCESS;

	case SLJIT_CLZ:
		SLJIT_ASSERT(src1 == TMP_REG1 && !(flags & SRC2_IMM));
#if (defined SLJIT_MIPS_R1 && SLJIT_MIPS_R1)
		if (op & SLJIT_SET_E)
			FAIL_IF(push_inst(compiler, SELECT_OP(DCLZ, CLZ) | S(src2) | TA(EQUAL_FLAG) | DA(EQUAL_FLAG), EQUAL_FLAG));
		if (CHECK_FLAGS(SLJIT_SET_E))
			FAIL_IF(push_inst(compiler, SELECT_OP(DCLZ, CLZ) | S(src2) | T(dst) | D(dst), DR(dst)));
#else
		if (SLJIT_UNLIKELY(flags & UNUSED_DEST)) {
			FAIL_IF(push_inst(compiler, SELECT_OP(DSRL32, SRL) | T(src2) | DA(EQUAL_FLAG) | SH_IMM(31), EQUAL_FLAG));
			return push_inst(compiler, XORI | SA(EQUAL_FLAG) | TA(EQUAL_FLAG) | IMM(1), EQUAL_FLAG);
		}
		/* Nearly all instructions are unmovable in the following sequence. */
		FAIL_IF(push_inst(compiler, SELECT_OP(DADDU, ADDU) | S(src2) | TA(0) | D(TMP_REG1), DR(TMP_REG1)));
		/* Check zero. */
		FAIL_IF(push_inst(compiler, BEQ | S(TMP_REG1) | TA(0) | IMM(5), UNMOVABLE_INS));
		FAIL_IF(push_inst(compiler, ORI | SA(0) | T(dst) | IMM((op & SLJIT_INT_OP) ? 32 : 64), UNMOVABLE_INS));
		FAIL_IF(push_inst(compiler, SELECT_OP(DADDIU, ADDIU) | SA(0) | T(dst) | IMM(-1), DR(dst)));
		/* Loop for searching the highest bit. */
		FAIL_IF(push_inst(compiler, SELECT_OP(DADDIU, ADDIU) | S(dst) | T(dst) | IMM(1), DR(dst)));
		FAIL_IF(push_inst(compiler, BGEZ | S(TMP_REG1) | IMM(-2), UNMOVABLE_INS));
		FAIL_IF(push_inst(compiler, SELECT_OP(DSLL, SLL) | T(TMP_REG1) | D(TMP_REG1) | SH_IMM(1), UNMOVABLE_INS));
		if (op & SLJIT_SET_E)
			return push_inst(compiler, SELECT_OP(DADDU, ADDU) | S(dst) | TA(0) | DA(EQUAL_FLAG), EQUAL_FLAG);
#endif
		return SLJIT_SUCCESS;

	case SLJIT_ADD:
		if (flags & SRC2_IMM) {
			if (op & SLJIT_SET_O) {
				if (src2 >= 0)
					FAIL_IF(push_inst(compiler, OR | S(src1) | T(src1) | DA(OVERFLOW_FLAG), OVERFLOW_FLAG));
				else
					FAIL_IF(push_inst(compiler, NOR | S(src1) | T(src1) | DA(OVERFLOW_FLAG), OVERFLOW_FLAG));
			}
			if (op & SLJIT_SET_E)
				FAIL_IF(push_inst(compiler, SELECT_OP(DADDIU, ADDIU) | S(src1) | TA(EQUAL_FLAG) | IMM(src2), EQUAL_FLAG));
			if (op & (SLJIT_SET_C | SLJIT_SET_O)) {
				if (src2 >= 0)
					FAIL_IF(push_inst(compiler, ORI | S(src1) | TA(ULESS_FLAG) | IMM(src2), ULESS_FLAG));
				else {
					FAIL_IF(push_inst(compiler, SELECT_OP(DADDIU, ADDIU) | SA(0) | TA(ULESS_FLAG) | IMM(src2), ULESS_FLAG));
					FAIL_IF(push_inst(compiler, OR | S(src1) | TA(ULESS_FLAG) | DA(ULESS_FLAG), ULESS_FLAG));
				}
			}
			/* dst may be the same as src1 or src2. */
			if (CHECK_FLAGS(SLJIT_SET_E))
				FAIL_IF(push_inst(compiler, SELECT_OP(DADDIU, ADDIU) | S(src1) | T(dst) | IMM(src2), DR(dst)));
		}
		else {
			if (op & SLJIT_SET_O)
				FAIL_IF(push_inst(compiler, XOR | S(src1) | T(src2) | DA(OVERFLOW_FLAG), OVERFLOW_FLAG));
			if (op & SLJIT_SET_E)
				FAIL_IF(push_inst(compiler, SELECT_OP(DADDU, ADDU) | S(src1) | T(src2) | DA(EQUAL_FLAG), EQUAL_FLAG));
			if (op & (SLJIT_SET_C | SLJIT_SET_O))
				FAIL_IF(push_inst(compiler, OR | S(src1) | T(src2) | DA(ULESS_FLAG), ULESS_FLAG));
			/* dst may be the same as src1 or src2. */
			if (CHECK_FLAGS(SLJIT_SET_E))
				FAIL_IF(push_inst(compiler, SELECT_OP(DADDU, ADDU) | S(src1) | T(src2) | D(dst), DR(dst)));
		}

		/* a + b >= a | b (otherwise, the carry should be set to 1). */
		if (op & (SLJIT_SET_C | SLJIT_SET_O))
			FAIL_IF(push_inst(compiler, SLTU | S(dst) | TA(ULESS_FLAG) | DA(ULESS_FLAG), ULESS_FLAG));
		if (!(op & SLJIT_SET_O))
			return SLJIT_SUCCESS;
		FAIL_IF(push_inst(compiler, SELECT_OP(DSLL32, SLL) | TA(ULESS_FLAG) | D(TMP_REG1) | SH_IMM(31), DR(TMP_REG1)));
		FAIL_IF(push_inst(compiler, XOR | S(TMP_REG1) | TA(OVERFLOW_FLAG) | DA(OVERFLOW_FLAG), OVERFLOW_FLAG));
		FAIL_IF(push_inst(compiler, XOR | S(dst) | TA(OVERFLOW_FLAG) | DA(OVERFLOW_FLAG), OVERFLOW_FLAG));
		return push_inst(compiler, SELECT_OP(DSRL32, SLL) | TA(OVERFLOW_FLAG) | DA(OVERFLOW_FLAG) | SH_IMM(31), OVERFLOW_FLAG);

	case SLJIT_ADDC:
		if (flags & SRC2_IMM) {
			if (op & SLJIT_SET_C) {
				if (src2 >= 0)
					FAIL_IF(push_inst(compiler, ORI | S(src1) | TA(OVERFLOW_FLAG) | IMM(src2), OVERFLOW_FLAG));
				else {
					FAIL_IF(push_inst(compiler, SELECT_OP(DADDIU, ADDIU) | SA(0) | TA(OVERFLOW_FLAG) | IMM(src2), OVERFLOW_FLAG));
					FAIL_IF(push_inst(compiler, OR | S(src1) | TA(OVERFLOW_FLAG) | DA(OVERFLOW_FLAG), OVERFLOW_FLAG));
				}
			}
			FAIL_IF(push_inst(compiler, SELECT_OP(DADDIU, ADDIU) | S(src1) | T(dst) | IMM(src2), DR(dst)));
		} else {
			if (op & SLJIT_SET_C)
				FAIL_IF(push_inst(compiler, OR | S(src1) | T(src2) | DA(OVERFLOW_FLAG), OVERFLOW_FLAG));
			/* dst may be the same as src1 or src2. */
			FAIL_IF(push_inst(compiler, SELECT_OP(DADDU, ADDU) | S(src1) | T(src2) | D(dst), DR(dst)));
		}
		if (op & SLJIT_SET_C)
			FAIL_IF(push_inst(compiler, SLTU | S(dst) | TA(OVERFLOW_FLAG) | DA(OVERFLOW_FLAG), OVERFLOW_FLAG));

		FAIL_IF(push_inst(compiler, SELECT_OP(DADDU, ADDU) | S(dst) | TA(ULESS_FLAG) | D(dst), DR(dst)));
		if (!(op & SLJIT_SET_C))
			return SLJIT_SUCCESS;

		/* Set ULESS_FLAG (dst == 0) && (ULESS_FLAG == 1). */
		FAIL_IF(push_inst(compiler, SLTU | S(dst) | TA(ULESS_FLAG) | DA(ULESS_FLAG), ULESS_FLAG));
		/* Set carry flag. */
		return push_inst(compiler, OR | SA(ULESS_FLAG) | TA(OVERFLOW_FLAG) | DA(ULESS_FLAG), ULESS_FLAG);

	case SLJIT_SUB:
		if ((flags & SRC2_IMM) && ((op & (SLJIT_SET_U | SLJIT_SET_S)) || src2 == SIMM_MIN)) {
			FAIL_IF(push_inst(compiler, ADDIU | SA(0) | T(TMP_REG2) | IMM(src2), DR(TMP_REG2)));
			src2 = TMP_REG2;
			flags &= ~SRC2_IMM;
		}

		if (flags & SRC2_IMM) {
			if (op & SLJIT_SET_O) {
				if (src2 >= 0)
					FAIL_IF(push_inst(compiler, OR | S(src1) | T(src1) | DA(OVERFLOW_FLAG), OVERFLOW_FLAG));
				else
					FAIL_IF(push_inst(compiler, NOR | S(src1) | T(src1) | DA(OVERFLOW_FLAG), OVERFLOW_FLAG));
			}
			if (op & SLJIT_SET_E)
				FAIL_IF(push_inst(compiler, SELECT_OP(DADDIU, ADDIU) | S(src1) | TA(EQUAL_FLAG) | IMM(-src2), EQUAL_FLAG));
			if (op & (SLJIT_SET_C | SLJIT_SET_O))
				FAIL_IF(push_inst(compiler, SLTIU | S(src1) | TA(ULESS_FLAG) | IMM(src2), ULESS_FLAG));
			/* dst may be the same as src1 or src2. */
			if (CHECK_FLAGS(SLJIT_SET_E))
				FAIL_IF(push_inst(compiler, SELECT_OP(DADDIU, ADDIU) | S(src1) | T(dst) | IMM(-src2), DR(dst)));
		}
		else {
			if (op & SLJIT_SET_O)
				FAIL_IF(push_inst(compiler, XOR | S(src1) | T(src2) | DA(OVERFLOW_FLAG), OVERFLOW_FLAG));
			if (op & SLJIT_SET_E)
				FAIL_IF(push_inst(compiler, SELECT_OP(DSUBU, SUBU) | S(src1) | T(src2) | DA(EQUAL_FLAG), EQUAL_FLAG));
			if (op & (SLJIT_SET_U | SLJIT_SET_C | SLJIT_SET_O))
				FAIL_IF(push_inst(compiler, SLTU | S(src1) | T(src2) | DA(ULESS_FLAG), ULESS_FLAG));
			if (op & SLJIT_SET_U)
				FAIL_IF(push_inst(compiler, SLTU | S(src2) | T(src1) | DA(UGREATER_FLAG), UGREATER_FLAG));
			if (op & SLJIT_SET_S) {
				FAIL_IF(push_inst(compiler, SLT | S(src1) | T(src2) | DA(LESS_FLAG), LESS_FLAG));
				FAIL_IF(push_inst(compiler, SLT | S(src2) | T(src1) | DA(GREATER_FLAG), GREATER_FLAG));
			}
			/* dst may be the same as src1 or src2. */
			if (CHECK_FLAGS(SLJIT_SET_E | SLJIT_SET_U | SLJIT_SET_S | SLJIT_SET_C))
				FAIL_IF(push_inst(compiler, SELECT_OP(DSUBU, SUBU) | S(src1) | T(src2) | D(dst), DR(dst)));
		}

		if (!(op & SLJIT_SET_O))
			return SLJIT_SUCCESS;
		FAIL_IF(push_inst(compiler, SELECT_OP(DSLL32, SLL) | TA(ULESS_FLAG) | D(TMP_REG1) | SH_IMM(31), DR(TMP_REG1)));
		FAIL_IF(push_inst(compiler, XOR | S(TMP_REG1) | TA(OVERFLOW_FLAG) | DA(OVERFLOW_FLAG), OVERFLOW_FLAG));
		FAIL_IF(push_inst(compiler, XOR | S(dst) | TA(OVERFLOW_FLAG) | DA(OVERFLOW_FLAG), OVERFLOW_FLAG));
		return push_inst(compiler, SELECT_OP(DSRL32, SRL) | TA(OVERFLOW_FLAG) | DA(OVERFLOW_FLAG) | SH_IMM(31), OVERFLOW_FLAG);

	case SLJIT_SUBC:
		if ((flags & SRC2_IMM) && src2 == SIMM_MIN) {
			FAIL_IF(push_inst(compiler, ADDIU | SA(0) | T(TMP_REG2) | IMM(src2), DR(TMP_REG2)));
			src2 = TMP_REG2;
			flags &= ~SRC2_IMM;
		}

		if (flags & SRC2_IMM) {
			if (op & SLJIT_SET_C)
				FAIL_IF(push_inst(compiler, SLTIU | S(src1) | TA(OVERFLOW_FLAG) | IMM(src2), OVERFLOW_FLAG));
			/* dst may be the same as src1 or src2. */
			FAIL_IF(push_inst(compiler, SELECT_OP(DADDIU, ADDIU) | S(src1) | T(dst) | IMM(-src2), DR(dst)));
		}
		else {
			if (op & SLJIT_SET_C)
				FAIL_IF(push_inst(compiler, SLTU | S(src1) | T(src2) | DA(OVERFLOW_FLAG), OVERFLOW_FLAG));
			/* dst may be the same as src1 or src2. */
			FAIL_IF(push_inst(compiler, SELECT_OP(DSUBU, SUBU) | S(src1) | T(src2) | D(dst), DR(dst)));
		}

		if (op & SLJIT_SET_C)
			FAIL_IF(push_inst(compiler, SLTU | S(dst) | TA(ULESS_FLAG) | DA(LESS_FLAG), LESS_FLAG));

		FAIL_IF(push_inst(compiler, SELECT_OP(DSUBU, SUBU) | S(dst) | TA(ULESS_FLAG) | D(dst), DR(dst)));
		return (op & SLJIT_SET_C) ? push_inst(compiler, OR | SA(OVERFLOW_FLAG) | TA(LESS_FLAG) | DA(ULESS_FLAG), ULESS_FLAG) : SLJIT_SUCCESS;

	case SLJIT_MUL:
		SLJIT_ASSERT(!(flags & SRC2_IMM));
		if (!(op & SLJIT_SET_O)) {
#if (defined SLJIT_MIPS_R1 && SLJIT_MIPS_R1)
			if (op & SLJIT_INT_OP)
				return push_inst(compiler, MUL | S(src1) | T(src2) | D(dst), DR(dst));
			FAIL_IF(push_inst(compiler, DMULT | S(src1) | T(src2), MOVABLE_INS));
			return push_inst(compiler, MFLO | D(dst), DR(dst));
#else
			FAIL_IF(push_inst(compiler, SELECT_OP(DMULT, MULT) | S(src1) | T(src2), MOVABLE_INS));
			return push_inst(compiler, MFLO | D(dst), DR(dst));
#endif
		}
		FAIL_IF(push_inst(compiler, SELECT_OP(DMULT, MULT) | S(src1) | T(src2), MOVABLE_INS));
		FAIL_IF(push_inst(compiler, MFHI | DA(ULESS_FLAG), ULESS_FLAG));
		FAIL_IF(push_inst(compiler, MFLO | D(dst), DR(dst)));
		FAIL_IF(push_inst(compiler, SELECT_OP(DSRA32, SRA) | T(dst) | DA(UGREATER_FLAG) | SH_IMM(31), UGREATER_FLAG));
		return push_inst(compiler, SELECT_OP(DSUBU, SUBU) | SA(ULESS_FLAG) | TA(UGREATER_FLAG) | DA(OVERFLOW_FLAG), OVERFLOW_FLAG);

	case SLJIT_AND:
		EMIT_LOGICAL(ANDI, AND);
		return SLJIT_SUCCESS;

	case SLJIT_OR:
		EMIT_LOGICAL(ORI, OR);
		return SLJIT_SUCCESS;

	case SLJIT_XOR:
		EMIT_LOGICAL(XORI, XOR);
		return SLJIT_SUCCESS;

	case SLJIT_SHL:
		EMIT_SHIFT(DSLL, DSLL32, SLL, DSLLV, SLLV);
		return SLJIT_SUCCESS;

	case SLJIT_LSHR:
		EMIT_SHIFT(DSRL, DSRL32, SRL, DSRLV, SRLV);
		return SLJIT_SUCCESS;

	case SLJIT_ASHR:
		EMIT_SHIFT(DSRA, DSRA32, SRA, DSRAV, SRAV);
		return SLJIT_SUCCESS;
	}

	SLJIT_ASSERT_STOP();
	return SLJIT_SUCCESS;
}

static SLJIT_INLINE sljit_si emit_const(struct sljit_compiler *compiler, sljit_si dst, sljit_sw init_value)
{
	FAIL_IF(push_inst(compiler, LUI | T(dst) | IMM(init_value >> 48), DR(dst)));
	FAIL_IF(push_inst(compiler, ORI | S(dst) | T(dst) | IMM(init_value >> 32), DR(dst)));
	FAIL_IF(push_inst(compiler, DSLL | T(dst) | D(dst) | SH_IMM(16), DR(dst)));
	FAIL_IF(push_inst(compiler, ORI | S(dst) | T(dst) | IMM(init_value >> 16), DR(dst)));
	FAIL_IF(push_inst(compiler, DSLL | T(dst) | D(dst) | SH_IMM(16), DR(dst)));
	return push_inst(compiler, ORI | S(dst) | T(dst) | IMM(init_value), DR(dst));
}

SLJIT_API_FUNC_ATTRIBUTE void sljit_set_jump_addr(sljit_uw addr, sljit_uw new_addr)
{
	sljit_ins *inst = (sljit_ins*)addr;

	inst[0] = (inst[0] & 0xffff0000) | ((new_addr >> 48) & 0xffff);
	inst[1] = (inst[1] & 0xffff0000) | ((new_addr >> 32) & 0xffff);
	inst[3] = (inst[3] & 0xffff0000) | ((new_addr >> 16) & 0xffff);
	inst[5] = (inst[5] & 0xffff0000) | (new_addr & 0xffff);
	SLJIT_CACHE_FLUSH(inst, inst + 6);
}

SLJIT_API_FUNC_ATTRIBUTE void sljit_set_const(sljit_uw addr, sljit_sw new_constant)
{
	sljit_ins *inst = (sljit_ins*)addr;

	inst[0] = (inst[0] & 0xffff0000) | ((new_constant >> 48) & 0xffff);
	inst[1] = (inst[1] & 0xffff0000) | ((new_constant >> 32) & 0xffff);
	inst[3] = (inst[3] & 0xffff0000) | ((new_constant >> 16) & 0xffff);
	inst[5] = (inst[5] & 0xffff0000) | (new_constant & 0xffff);
	SLJIT_CACHE_FLUSH(inst, inst + 6);
}


<|start_filename|>Zend/zend_exceptions.h<|end_filename|>
/*
   +----------------------------------------------------------------------+
   | Zend Engine                                                          |
   +----------------------------------------------------------------------+
   | Copyright (c) 1998-2015 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          |
   | <EMAIL> so we can mail you a copy immediately.              |
   +----------------------------------------------------------------------+
   | Authors: <NAME> <<EMAIL>>                                |
   |          <NAME> <<EMAIL>>                              |
   |          <NAME> <<EMAIL>>                          |
   |          <NAME> <<EMAIL>>                                |
   +----------------------------------------------------------------------+
*/

/* $Id$ */

#ifndef ZEND_EXCEPTIONS_H
#define ZEND_EXCEPTIONS_H

BEGIN_EXTERN_C()

ZEND_API void zend_exception_set_previous(zval *exception, zval *add_previous TSRMLS_DC);
ZEND_API void zend_exception_save(TSRMLS_D);
ZEND_API void zend_exception_restore(TSRMLS_D);

void zend_throw_exception_internal(zval *exception TSRMLS_DC);

void zend_register_default_exception(TSRMLS_D);

ZEND_API zend_class_entry *zend_exception_get_default(TSRMLS_D);
ZEND_API zend_class_entry *zend_get_error_exception(TSRMLS_D);
ZEND_API void zend_register_default_classes(TSRMLS_D);

/* exception_ce   NULL or zend_exception_get_default() or a derived class
 * message        NULL or the message of the exception */
ZEND_API zval * zend_throw_exception(zend_class_entry *exception_ce, const char *message, long code TSRMLS_DC);
ZEND_API zval * zend_throw_exception_ex(zend_class_entry *exception_ce, long code TSRMLS_DC, const char *format, ...);
ZEND_API void zend_throw_exception_object(zval *exception TSRMLS_DC);
ZEND_API void zend_clear_exception(TSRMLS_D);

ZEND_API zval * zend_throw_error_exception(zend_class_entry *exception_ce, const char *message, long code, int severity TSRMLS_DC);

extern ZEND_API void (*zend_throw_exception_hook)(zval *ex TSRMLS_DC);

/* show an exception using zend_error(severity,...), severity should be E_ERROR */
ZEND_API void zend_exception_error(zval *exception, int severity TSRMLS_DC);

/* do not export, in php it's available thru spprintf directly */
int zend_spprintf(char **message, int max_len, const char *format, ...);

END_EXTERN_C()

#endif

/*
 * Local variables:
 * tab-width: 4
 * c-basic-offset: 4
 * indent-tabs-mode: t
 * End:
 */


<|start_filename|>ext/date/lib/interval.c<|end_filename|>
/*
   +----------------------------------------------------------------------+
   | PHP Version 5                                                        |
   +----------------------------------------------------------------------+
   | Copyright (c) 1997-2015 The PHP Group                                |
   +----------------------------------------------------------------------+
   | This source file is subject to version 3.01 of the PHP 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.php.net/license/3_01.txt                                  |
   | If you did not receive a copy of the PHP license and are unable to   |
   | obtain it through the world-wide-web, please send a note to          |
   | <EMAIL> so we can mail you a copy immediately.               |
   +----------------------------------------------------------------------+
   | Authors: <NAME> <<EMAIL>>                    |
   +----------------------------------------------------------------------+
 */

/* $Id$ */

#include "timelib.h"
#include <math.h>

timelib_rel_time *timelib_diff(timelib_time *one, timelib_time *two)
{
	timelib_rel_time *rt;
	timelib_time *swp;
	timelib_sll dst_corr = 0 ,dst_h_corr = 0, dst_m_corr = 0;
	timelib_time one_backup, two_backup;

	rt = timelib_rel_time_ctor();
	rt->invert = 0;
	if (one->sse > two->sse) {
		swp = two;
		two = one;
		one = swp;
		rt->invert = 1;
	}

	/* Calculate correction for DST change over, but only if the TZ type is ID
	 * and it's the same */
	if (one->zone_type == 3 && two->zone_type == 3
		&& (strcmp(one->tz_info->name, two->tz_info->name) == 0)
		&& (one->z != two->z))
	{
		dst_corr = two->z - one->z;
		dst_h_corr = dst_corr / 3600;
		dst_m_corr = (dst_corr % 3600) / 60;
	}

	/* Save old TZ info */
	memcpy(&one_backup, one, sizeof(one_backup));
	memcpy(&two_backup, two, sizeof(two_backup));

    timelib_apply_localtime(one, 0);
    timelib_apply_localtime(two, 0);

	rt->y = two->y - one->y;
	rt->m = two->m - one->m;
	rt->d = two->d - one->d;
	rt->h = two->h - one->h;
	rt->i = two->i - one->i;
	rt->s = two->s - one->s;
	if (one_backup.dst == 0 && two_backup.dst == 1 && two->sse >= one->sse + 86400 - dst_corr) {
		rt->h += dst_h_corr;
		rt->i += dst_m_corr;
	}

	rt->days = abs(floor((one->sse - two->sse - (dst_h_corr * 3600) - (dst_m_corr * 60)) / 86400));

	timelib_do_rel_normalize(rt->invert ? one : two, rt);

	/* We need to do this after normalisation otherwise we can't get "24H" */
	if (one_backup.dst == 1 && two_backup.dst == 0 && two->sse >= one->sse + 86400) {
		if (two->sse < one->sse + 86400 - dst_corr) {
			rt->d--;
			rt->h = 24;
		} else {
			rt->h += dst_h_corr;
			rt->i += dst_m_corr;
		}
	}

	/* Restore old TZ info */
	memcpy(one, &one_backup, sizeof(one_backup));
	memcpy(two, &two_backup, sizeof(two_backup));

	return rt;
}

timelib_time *timelib_add(timelib_time *old_time, timelib_rel_time *interval)
{
	int bias = 1;
	timelib_time *t = timelib_time_clone(old_time);

	if (interval->have_weekday_relative || interval->have_special_relative) {
		memcpy(&t->relative, interval, sizeof(struct timelib_rel_time));
	} else {
		if (interval->invert) {
			bias = -1;
		}
		memset(&t->relative, 0, sizeof(struct timelib_rel_time));
		t->relative.y = interval->y * bias;
		t->relative.m = interval->m * bias;
		t->relative.d = interval->d * bias;
		t->relative.h = interval->h * bias;
		t->relative.i = interval->i * bias;
		t->relative.s = interval->s * bias;
	}
	t->have_relative = 1;
	t->sse_uptodate = 0;

	timelib_update_ts(t, NULL);

//	printf("%lld %lld %d\n", old_time->dst, t->dst, (t->sse - old_time->sse));
	/* Adjust for backwards DST changeover */
	if (old_time->dst == 1 && t->dst == 0 && !interval->y && !interval->m && !interval->d) {
		t->sse -= old_time->z;
		t->sse += t->z;
	}

	timelib_update_from_sse(t);
	t->have_relative = 0;

	return t;
}

timelib_time *timelib_sub(timelib_time *old_time, timelib_rel_time *interval)
{
	int bias = 1;
	timelib_time *t = timelib_time_clone(old_time);

	if (interval->invert) {
		bias = -1;
	}

	memset(&t->relative, 0, sizeof(struct timelib_rel_time));
	t->relative.y = 0 - (interval->y * bias);
	t->relative.m = 0 - (interval->m * bias);
	t->relative.d = 0 - (interval->d * bias);
	t->relative.h = 0 - (interval->h * bias);
	t->relative.i = 0 - (interval->i * bias);
	t->relative.s = 0 - (interval->s * bias);
	t->have_relative = 1;
	t->sse_uptodate = 0;

	timelib_update_ts(t, NULL);

	/* Adjust for backwards DST changeover */
	if (old_time->dst == 1 && t->dst == 0 && !interval->y && !interval->m && !interval->d) {
		t->sse -= old_time->z;
		t->sse += t->z;
	}
	/* Adjust for forwards DST changeover */
	if (old_time->dst == 0 && t->dst == 1 && !interval->y && !interval->m && !interval->d ) {
		t->sse -= old_time->z;
		t->sse += t->z;
	}

	timelib_update_from_sse(t);

	t->have_relative = 0;

	return t;
}


<|start_filename|>Zend/zend_alloc.c<|end_filename|>
/*
   +----------------------------------------------------------------------+
   | Zend Engine                                                          |
   +----------------------------------------------------------------------+
   | Copyright (c) 1998-2015 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          |
   | <EMAIL> so we can mail you a copy immediately.              |
   +----------------------------------------------------------------------+
   | Authors: <NAME> <<EMAIL>>                                |
   |          <NAME> <<EMAIL>>                                |
   |          <NAME> <<EMAIL>>                             |
   +----------------------------------------------------------------------+
*/

/* $Id$ */

#include "zend.h"
#include "zend_alloc.h"
#include "zend_globals.h"
#include "zend_operators.h"

#ifdef HAVE_SIGNAL_H
# include <signal.h>
#endif
#ifdef HAVE_UNISTD_H
# include <unistd.h>
#endif

#ifdef ZEND_WIN32
# include <wincrypt.h>
# include <process.h>
#endif

#ifndef ZEND_MM_HEAP_PROTECTION
# define ZEND_MM_HEAP_PROTECTION ZEND_DEBUG
#endif

#ifndef ZEND_MM_SAFE_UNLINKING
# define ZEND_MM_SAFE_UNLINKING 1
#endif

#ifndef ZEND_MM_COOKIES
# define ZEND_MM_COOKIES ZEND_DEBUG
#endif

#ifdef _WIN64
# define PTR_FMT "0x%0.16I64x"
/*
#elif sizeof(long) == 8
# define PTR_FMT "0x%0.16lx"
*/
#else
# define PTR_FMT "0x%0.8lx"
#endif

#if ZEND_DEBUG
void zend_debug_alloc_output(char *format, ...)
{
	char output_buf[256];
	va_list args;

	va_start(args, format);
	vsprintf(output_buf, format, args);
	va_end(args);

#ifdef ZEND_WIN32
	OutputDebugString(output_buf);
#else
	fprintf(stderr, "%s", output_buf);
#endif
}
#endif

#if (defined (__GNUC__) && __GNUC__ > 2 ) && !defined(__INTEL_COMPILER) && !defined(DARWIN) && !defined(__hpux) && !defined(_AIX)
static void zend_mm_panic(const char *message) __attribute__ ((noreturn));
#endif

static void zend_mm_panic(const char *message)
{
	fprintf(stderr, "%s\n", message);
/* See http://support.microsoft.com/kb/190351 */
#ifdef PHP_WIN32
	fflush(stderr);
#endif
#if ZEND_DEBUG && defined(HAVE_KILL) && defined(HAVE_GETPID)
	kill(getpid(), SIGSEGV);
#endif
	exit(1);
}

/*******************/
/* Storage Manager */
/*******************/

#ifdef ZEND_WIN32
#  define HAVE_MEM_WIN32    /* use VirtualAlloc() to allocate memory     */
#endif
#define HAVE_MEM_MALLOC     /* use malloc() to allocate segments         */

#include <sys/types.h>
#include <sys/stat.h>
#if HAVE_LIMITS_H
#include <limits.h>
#endif
#include <fcntl.h>
#include <errno.h>

#if defined(HAVE_MEM_MMAP_ANON) || defined(HAVE_MEM_MMAP_ZERO)
# ifdef HAVE_MREMAP
#  ifndef _GNU_SOURCE
#   define _GNU_SOURCE
#  endif
#  ifndef __USE_GNU
#   define __USE_GNU
#  endif
# endif
# include <sys/mman.h>
# ifndef MAP_ANON
#  ifdef MAP_ANONYMOUS
#   define MAP_ANON MAP_ANONYMOUS
#  endif
# endif
# ifndef MREMAP_MAYMOVE
#  define MREMAP_MAYMOVE 0
# endif
# ifndef MAP_FAILED
#  define MAP_FAILED ((void*)-1)
# endif
#endif

static zend_mm_storage* zend_mm_mem_dummy_init(void *params)
{
	return malloc(sizeof(zend_mm_storage));
}

static void zend_mm_mem_dummy_dtor(zend_mm_storage *storage)
{
	free(storage);
}

static void zend_mm_mem_dummy_compact(zend_mm_storage *storage)
{
}

#if defined(HAVE_MEM_MMAP_ANON) || defined(HAVE_MEM_MMAP_ZERO)

static zend_mm_segment* zend_mm_mem_mmap_realloc(zend_mm_storage *storage, zend_mm_segment* segment, size_t size)
{
	zend_mm_segment *ret;
#ifdef HAVE_MREMAP
#if defined(__NetBSD__)
	/* NetBSD 5 supports mremap but takes an extra newp argument */
	ret = (zend_mm_segment*)mremap(segment, segment->size, segment, size, MREMAP_MAYMOVE);
#else
	ret = (zend_mm_segment*)mremap(segment, segment->size, size, MREMAP_MAYMOVE);
#endif
	if (ret == MAP_FAILED) {
#endif
		ret = storage->handlers->_alloc(storage, size);
		if (ret) {
			memcpy(ret, segment, size > segment->size ? segment->size : size);
			storage->handlers->_free(storage, segment);
		}
#ifdef HAVE_MREMAP
	}
#endif
	return ret;
}

static void zend_mm_mem_mmap_free(zend_mm_storage *storage, zend_mm_segment* segment)
{
	munmap((void*)segment, segment->size);
}

#endif

#ifdef HAVE_MEM_MMAP_ANON

static zend_mm_segment* zend_mm_mem_mmap_anon_alloc(zend_mm_storage *storage, size_t size)
{
	zend_mm_segment *ret = (zend_mm_segment*)mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0);
	if (ret == MAP_FAILED) {
		ret = NULL;
	}
	return ret;
}

# define ZEND_MM_MEM_MMAP_ANON_DSC {"mmap_anon", zend_mm_mem_dummy_init, zend_mm_mem_dummy_dtor, zend_mm_mem_dummy_compact, zend_mm_mem_mmap_anon_alloc, zend_mm_mem_mmap_realloc, zend_mm_mem_mmap_free}

#endif

#ifdef HAVE_MEM_MMAP_ZERO

static int zend_mm_dev_zero_fd = -1;

static zend_mm_storage* zend_mm_mem_mmap_zero_init(void *params)
{
	if (zend_mm_dev_zero_fd == -1) {
		zend_mm_dev_zero_fd = open("/dev/zero", O_RDWR, S_IRUSR | S_IWUSR);
	}
	if (zend_mm_dev_zero_fd >= 0) {
		return malloc(sizeof(zend_mm_storage));
	} else {
		return NULL;
	}
}

static void zend_mm_mem_mmap_zero_dtor(zend_mm_storage *storage)
{
	close(zend_mm_dev_zero_fd);
	free(storage);
}

static zend_mm_segment* zend_mm_mem_mmap_zero_alloc(zend_mm_storage *storage, size_t size)
{
	zend_mm_segment *ret = (zend_mm_segment*)mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_PRIVATE, zend_mm_dev_zero_fd, 0);
	if (ret == MAP_FAILED) {
		ret = NULL;
	}
	return ret;
}

# define ZEND_MM_MEM_MMAP_ZERO_DSC {"mmap_zero", zend_mm_mem_mmap_zero_init, zend_mm_mem_mmap_zero_dtor, zend_mm_mem_dummy_compact, zend_mm_mem_mmap_zero_alloc, zend_mm_mem_mmap_realloc, zend_mm_mem_mmap_free}

#endif

#ifdef HAVE_MEM_WIN32

static zend_mm_storage* zend_mm_mem_win32_init(void *params)
{
	HANDLE heap = HeapCreate(HEAP_NO_SERIALIZE, 0, 0);
	zend_mm_storage* storage;

	if (heap == NULL) {
		return NULL;
	}
	storage = (zend_mm_storage*)malloc(sizeof(zend_mm_storage));
	if (storage == NULL) {
		HeapDestroy(heap);
		return NULL;
	}
	storage->data = (void*) heap;
	return storage;
}

static void zend_mm_mem_win32_dtor(zend_mm_storage *storage)
{
	HeapDestroy((HANDLE)storage->data);
	free(storage);
}

static void zend_mm_mem_win32_compact(zend_mm_storage *storage)
{
    HeapDestroy((HANDLE)storage->data);
    storage->data = (void*)HeapCreate(HEAP_NO_SERIALIZE, 0, 0);
}

static zend_mm_segment* zend_mm_mem_win32_alloc(zend_mm_storage *storage, size_t size)
{
	return (zend_mm_segment*) HeapAlloc((HANDLE)storage->data, HEAP_NO_SERIALIZE, size);
}

static void zend_mm_mem_win32_free(zend_mm_storage *storage, zend_mm_segment* segment)
{
	HeapFree((HANDLE)storage->data, HEAP_NO_SERIALIZE, segment);
}

static zend_mm_segment* zend_mm_mem_win32_realloc(zend_mm_storage *storage, zend_mm_segment* segment, size_t size)
{
	return (zend_mm_segment*) HeapReAlloc((HANDLE)storage->data, HEAP_NO_SERIALIZE, segment, size);
}

# define ZEND_MM_MEM_WIN32_DSC {"win32", zend_mm_mem_win32_init, zend_mm_mem_win32_dtor, zend_mm_mem_win32_compact, zend_mm_mem_win32_alloc, zend_mm_mem_win32_realloc, zend_mm_mem_win32_free}

#endif

#ifdef HAVE_MEM_MALLOC

static zend_mm_segment* zend_mm_mem_malloc_alloc(zend_mm_storage *storage, size_t size)
{
	return (zend_mm_segment*)malloc(size);
}

static zend_mm_segment* zend_mm_mem_malloc_realloc(zend_mm_storage *storage, zend_mm_segment *ptr, size_t size)
{
	return (zend_mm_segment*)realloc(ptr, size);
}

static void zend_mm_mem_malloc_free(zend_mm_storage *storage, zend_mm_segment *ptr)
{
	free(ptr);
}

# define ZEND_MM_MEM_MALLOC_DSC {"malloc", zend_mm_mem_dummy_init, zend_mm_mem_dummy_dtor, zend_mm_mem_dummy_compact, zend_mm_mem_malloc_alloc, zend_mm_mem_malloc_realloc, zend_mm_mem_malloc_free}

#endif

static const zend_mm_mem_handlers mem_handlers[] = {
#ifdef HAVE_MEM_WIN32
	ZEND_MM_MEM_WIN32_DSC,
#endif
#ifdef HAVE_MEM_MALLOC
	ZEND_MM_MEM_MALLOC_DSC,
#endif
#ifdef HAVE_MEM_MMAP_ANON
	ZEND_MM_MEM_MMAP_ANON_DSC,
#endif
#ifdef HAVE_MEM_MMAP_ZERO
	ZEND_MM_MEM_MMAP_ZERO_DSC,
#endif
	{NULL, NULL, NULL, NULL, NULL, NULL}
};

# define ZEND_MM_STORAGE_DTOR()						heap->storage->handlers->dtor(heap->storage)
# define ZEND_MM_STORAGE_ALLOC(size)				heap->storage->handlers->_alloc(heap->storage, size)
# define ZEND_MM_STORAGE_REALLOC(ptr, size)			heap->storage->handlers->_realloc(heap->storage, ptr, size)
# define ZEND_MM_STORAGE_FREE(ptr)					heap->storage->handlers->_free(heap->storage, ptr)

/****************/
/* Heap Manager */
/****************/

#define MEM_BLOCK_VALID  0x7312F8DC
#define	MEM_BLOCK_FREED  0x99954317
#define	MEM_BLOCK_CACHED 0xFB8277DC
#define	MEM_BLOCK_GUARD  0x2A8FCC84
#define	MEM_BLOCK_LEAK   0x6C5E8F2D

/* mm block type */
typedef struct _zend_mm_block_info {
#if ZEND_MM_COOKIES
	size_t _cookie;
#endif
	size_t _size;
	size_t _prev;
} zend_mm_block_info;

#if ZEND_DEBUG

typedef struct _zend_mm_debug_info {
	const char *filename;
	uint lineno;
	const char *orig_filename;
	uint orig_lineno;
	size_t size;
#if ZEND_MM_HEAP_PROTECTION
	unsigned int start_magic;
#endif
} zend_mm_debug_info;

#elif ZEND_MM_HEAP_PROTECTION

typedef struct _zend_mm_debug_info {
	size_t size;
	unsigned int start_magic;
} zend_mm_debug_info;

#endif

typedef struct _zend_mm_block {
	zend_mm_block_info info;
#if ZEND_DEBUG
	unsigned int magic;
# ifdef ZTS
	THREAD_T thread_id;
# endif
	zend_mm_debug_info debug;
#elif ZEND_MM_HEAP_PROTECTION
	zend_mm_debug_info debug;
#endif
} zend_mm_block;

typedef struct _zend_mm_small_free_block {
	zend_mm_block_info info;
#if ZEND_DEBUG
	unsigned int magic;
# ifdef ZTS
	THREAD_T thread_id;
# endif
#endif
	struct _zend_mm_free_block *prev_free_block;
	struct _zend_mm_free_block *next_free_block;
} zend_mm_small_free_block;

typedef struct _zend_mm_free_block {
	zend_mm_block_info info;
#if ZEND_DEBUG
	unsigned int magic;
# ifdef ZTS
	THREAD_T thread_id;
# endif
#endif
	struct _zend_mm_free_block *prev_free_block;
	struct _zend_mm_free_block *next_free_block;

	struct _zend_mm_free_block **parent;
	struct _zend_mm_free_block *child[2];
} zend_mm_free_block;

#define ZEND_MM_NUM_BUCKETS (sizeof(size_t) << 3)

#define ZEND_MM_CACHE 1
#define ZEND_MM_CACHE_SIZE (ZEND_MM_NUM_BUCKETS * 4 * 1024)

#ifndef ZEND_MM_CACHE_STAT
# define ZEND_MM_CACHE_STAT 0
#endif

struct _zend_mm_heap {
	int                 use_zend_alloc;
	void               *(*_malloc)(size_t);
	void                (*_free)(void*);
	void               *(*_realloc)(void*, size_t);
	size_t              free_bitmap;
	size_t              large_free_bitmap;
	size_t              block_size;
	size_t              compact_size;
	zend_mm_segment    *segments_list;
	zend_mm_storage    *storage;
	size_t              real_size;
	size_t              real_peak;
	size_t              limit;
	size_t              size;
	size_t              peak;
	size_t              reserve_size;
	void               *reserve;
	int                 overflow;
	int                 internal;
#if ZEND_MM_CACHE
	unsigned int        cached;
	zend_mm_free_block *cache[ZEND_MM_NUM_BUCKETS];
#endif
	zend_mm_free_block *free_buckets[ZEND_MM_NUM_BUCKETS*2];
	zend_mm_free_block *large_free_buckets[ZEND_MM_NUM_BUCKETS];
	zend_mm_free_block *rest_buckets[2];
	int                 rest_count;
#if ZEND_MM_CACHE_STAT
	struct {
		int count;
		int max_count;
		int hit;
		int miss;
	} cache_stat[ZEND_MM_NUM_BUCKETS+1];
#endif
};

#define ZEND_MM_SMALL_FREE_BUCKET(heap, index) \
	(zend_mm_free_block*) ((char*)&heap->free_buckets[index * 2] + \
		sizeof(zend_mm_free_block*) * 2 - \
		sizeof(zend_mm_small_free_block))

#define ZEND_MM_REST_BUCKET(heap) \
	(zend_mm_free_block*)((char*)&heap->rest_buckets[0] + \
		sizeof(zend_mm_free_block*) * 2 - \
		sizeof(zend_mm_small_free_block))

#define ZEND_MM_REST_BLOCK ((zend_mm_free_block**)(zend_uintptr_t)(1))

#define ZEND_MM_MAX_REST_BLOCKS 16

#if ZEND_MM_COOKIES

static unsigned int _zend_mm_cookie = 0;

# define ZEND_MM_COOKIE(block) \
	(((size_t)(block)) ^ _zend_mm_cookie)
# define ZEND_MM_SET_COOKIE(block) \
	(block)->info._cookie = ZEND_MM_COOKIE(block)
# define ZEND_MM_CHECK_COOKIE(block) \
	if (UNEXPECTED((block)->info._cookie != ZEND_MM_COOKIE(block))) { \
		zend_mm_panic("zend_mm_heap corrupted"); \
	}
#else
# define ZEND_MM_SET_COOKIE(block)
# define ZEND_MM_CHECK_COOKIE(block)
#endif

/* Default memory segment size */
#define ZEND_MM_SEG_SIZE   (256 * 1024)

/* Reserved space for error reporting in case of memory overflow */
#define ZEND_MM_RESERVE_SIZE            (8*1024)

#ifdef _WIN64
# define ZEND_MM_LONG_CONST(x)	(x##i64)
#else
# define ZEND_MM_LONG_CONST(x)	(x##L)
#endif

#define ZEND_MM_TYPE_MASK		ZEND_MM_LONG_CONST(0x3)

#define ZEND_MM_FREE_BLOCK		ZEND_MM_LONG_CONST(0x0)
#define ZEND_MM_USED_BLOCK		ZEND_MM_LONG_CONST(0x1)
#define ZEND_MM_GUARD_BLOCK		ZEND_MM_LONG_CONST(0x3)

#define ZEND_MM_BLOCK(b, type, size)	do { \
											size_t _size = (size); \
											(b)->info._size = (type) | _size; \
											ZEND_MM_BLOCK_AT(b, _size)->info._prev = (type) | _size; \
											ZEND_MM_SET_COOKIE(b); \
										} while (0);
#define ZEND_MM_LAST_BLOCK(b)			do { \
		(b)->info._size = ZEND_MM_GUARD_BLOCK | ZEND_MM_ALIGNED_HEADER_SIZE; \
		ZEND_MM_SET_MAGIC(b, MEM_BLOCK_GUARD); \
 	} while (0);
#define ZEND_MM_BLOCK_SIZE(b)			((b)->info._size & ~ZEND_MM_TYPE_MASK)
#define ZEND_MM_IS_FREE_BLOCK(b)		(!((b)->info._size & ZEND_MM_USED_BLOCK))
#define ZEND_MM_IS_USED_BLOCK(b)		((b)->info._size & ZEND_MM_USED_BLOCK)
#define ZEND_MM_IS_GUARD_BLOCK(b)		(((b)->info._size & ZEND_MM_TYPE_MASK) == ZEND_MM_GUARD_BLOCK)

#define ZEND_MM_NEXT_BLOCK(b)			ZEND_MM_BLOCK_AT(b, ZEND_MM_BLOCK_SIZE(b))
#define ZEND_MM_PREV_BLOCK(b)			ZEND_MM_BLOCK_AT(b, -(ssize_t)((b)->info._prev & ~ZEND_MM_TYPE_MASK))

#define ZEND_MM_PREV_BLOCK_IS_FREE(b)	(!((b)->info._prev & ZEND_MM_USED_BLOCK))

#define ZEND_MM_MARK_FIRST_BLOCK(b)		((b)->info._prev = ZEND_MM_GUARD_BLOCK)
#define ZEND_MM_IS_FIRST_BLOCK(b)		((b)->info._prev == ZEND_MM_GUARD_BLOCK)

/* optimized access */
#define ZEND_MM_FREE_BLOCK_SIZE(b)		(b)->info._size

/* Aligned header size */
#define ZEND_MM_ALIGNED_HEADER_SIZE			ZEND_MM_ALIGNED_SIZE(sizeof(zend_mm_block))
#define ZEND_MM_ALIGNED_FREE_HEADER_SIZE	ZEND_MM_ALIGNED_SIZE(sizeof(zend_mm_small_free_block))
#define ZEND_MM_MIN_ALLOC_BLOCK_SIZE		ZEND_MM_ALIGNED_SIZE(ZEND_MM_ALIGNED_HEADER_SIZE + END_MAGIC_SIZE)
#define ZEND_MM_ALIGNED_MIN_HEADER_SIZE		(ZEND_MM_MIN_ALLOC_BLOCK_SIZE>ZEND_MM_ALIGNED_FREE_HEADER_SIZE?ZEND_MM_MIN_ALLOC_BLOCK_SIZE:ZEND_MM_ALIGNED_FREE_HEADER_SIZE)
#define ZEND_MM_ALIGNED_SEGMENT_SIZE		ZEND_MM_ALIGNED_SIZE(sizeof(zend_mm_segment))

#define ZEND_MM_MIN_SIZE					((ZEND_MM_ALIGNED_MIN_HEADER_SIZE>(ZEND_MM_ALIGNED_HEADER_SIZE+END_MAGIC_SIZE))?(ZEND_MM_ALIGNED_MIN_HEADER_SIZE-(ZEND_MM_ALIGNED_HEADER_SIZE+END_MAGIC_SIZE)):0)

#define ZEND_MM_MAX_SMALL_SIZE				((ZEND_MM_NUM_BUCKETS<<ZEND_MM_ALIGNMENT_LOG2)+ZEND_MM_ALIGNED_MIN_HEADER_SIZE)

#define ZEND_MM_TRUE_SIZE(size)				((size<ZEND_MM_MIN_SIZE)?(ZEND_MM_ALIGNED_MIN_HEADER_SIZE):(ZEND_MM_ALIGNED_SIZE(size+ZEND_MM_ALIGNED_HEADER_SIZE+END_MAGIC_SIZE)))

#define ZEND_MM_BUCKET_INDEX(true_size)		((true_size>>ZEND_MM_ALIGNMENT_LOG2)-(ZEND_MM_ALIGNED_MIN_HEADER_SIZE>>ZEND_MM_ALIGNMENT_LOG2))

#define ZEND_MM_SMALL_SIZE(true_size)		(true_size < ZEND_MM_MAX_SMALL_SIZE)

/* Memory calculations */
#define ZEND_MM_BLOCK_AT(blk, offset)	((zend_mm_block *) (((char *) (blk))+(offset)))
#define ZEND_MM_DATA_OF(p)				((void *) (((char *) (p))+ZEND_MM_ALIGNED_HEADER_SIZE))
#define ZEND_MM_HEADER_OF(blk)			ZEND_MM_BLOCK_AT(blk, -(int)ZEND_MM_ALIGNED_HEADER_SIZE)

/* Debug output */
#if ZEND_DEBUG

# ifdef ZTS
#  define ZEND_MM_SET_THREAD_ID(block) \
	((zend_mm_block*)(block))->thread_id = tsrm_thread_id()
#  define ZEND_MM_BAD_THREAD_ID(block) ((block)->thread_id != tsrm_thread_id())
# else
#  define ZEND_MM_SET_THREAD_ID(block)
#  define ZEND_MM_BAD_THREAD_ID(block) 0
# endif

# define ZEND_MM_VALID_PTR(block) \
	zend_mm_check_ptr(heap, block, 1 ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC)

# define ZEND_MM_SET_MAGIC(block, val) do { \
		(block)->magic = (val); \
	} while (0)

# define ZEND_MM_CHECK_MAGIC(block, val) do { \
		if ((block)->magic != (val)) { \
			zend_mm_panic("zend_mm_heap corrupted"); \
		} \
	} while (0)

# define ZEND_MM_SET_DEBUG_INFO(block, __size, set_valid, set_thread) do { \
		((zend_mm_block*)(block))->debug.filename = __zend_filename; \
		((zend_mm_block*)(block))->debug.lineno = __zend_lineno; \
		((zend_mm_block*)(block))->debug.orig_filename = __zend_orig_filename; \
		((zend_mm_block*)(block))->debug.orig_lineno = __zend_orig_lineno; \
		ZEND_MM_SET_BLOCK_SIZE(block, __size); \
		if (set_valid) { \
			ZEND_MM_SET_MAGIC(block, MEM_BLOCK_VALID); \
		} \
		if (set_thread) { \
			ZEND_MM_SET_THREAD_ID(block); \
		} \
	} while (0)

#else

# define ZEND_MM_VALID_PTR(ptr) EXPECTED(ptr != NULL)

# define ZEND_MM_SET_MAGIC(block, val)

# define ZEND_MM_CHECK_MAGIC(block, val)

# define ZEND_MM_SET_DEBUG_INFO(block, __size, set_valid, set_thread) ZEND_MM_SET_BLOCK_SIZE(block, __size)

#endif


#if ZEND_MM_HEAP_PROTECTION

# define ZEND_MM_CHECK_PROTECTION(block) \
	do { \
		if ((block)->debug.start_magic != _mem_block_start_magic || \
		    memcmp(ZEND_MM_END_MAGIC_PTR(block), &_mem_block_end_magic, END_MAGIC_SIZE) != 0) { \
		    zend_mm_panic("zend_mm_heap corrupted"); \
		} \
	} while (0)

# define ZEND_MM_END_MAGIC_PTR(block) \
	(((char*)(ZEND_MM_DATA_OF(block))) + ((zend_mm_block*)(block))->debug.size)

# define END_MAGIC_SIZE sizeof(unsigned int)

# define ZEND_MM_SET_BLOCK_SIZE(block, __size) do { \
		char *p; \
		((zend_mm_block*)(block))->debug.size = (__size); \
		p = ZEND_MM_END_MAGIC_PTR(block); \
		((zend_mm_block*)(block))->debug.start_magic = _mem_block_start_magic; \
		memcpy(p, &_mem_block_end_magic, END_MAGIC_SIZE); \
	} while (0)

static unsigned int _mem_block_start_magic = 0;
static unsigned int _mem_block_end_magic   = 0;

#else

# if ZEND_DEBUG
#  define ZEND_MM_SET_BLOCK_SIZE(block, _size) \
	((zend_mm_block*)(block))->debug.size = (_size)
# else
#  define ZEND_MM_SET_BLOCK_SIZE(block, _size)
# endif

# define ZEND_MM_CHECK_PROTECTION(block)

# define END_MAGIC_SIZE 0

#endif

#if ZEND_MM_SAFE_UNLINKING
# define ZEND_MM_CHECK_BLOCK_LINKAGE(block) \
	if (UNEXPECTED((block)->info._size != ZEND_MM_BLOCK_AT(block, ZEND_MM_FREE_BLOCK_SIZE(block))->info._prev) || \
		UNEXPECTED(!UNEXPECTED(ZEND_MM_IS_FIRST_BLOCK(block)) && \
	    UNEXPECTED(ZEND_MM_PREV_BLOCK(block)->info._size != (block)->info._prev))) { \
	    zend_mm_panic("zend_mm_heap corrupted"); \
	}
#define ZEND_MM_CHECK_TREE(block) \
	if (UNEXPECTED(*((block)->parent) != (block))) { \
		zend_mm_panic("zend_mm_heap corrupted"); \
	}
#else
# define ZEND_MM_CHECK_BLOCK_LINKAGE(block)
# define ZEND_MM_CHECK_TREE(block)
#endif

#define ZEND_MM_LARGE_BUCKET_INDEX(S) zend_mm_high_bit(S)

static void *_zend_mm_alloc_int(zend_mm_heap *heap, size_t size ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC) ZEND_ATTRIBUTE_MALLOC ZEND_ATTRIBUTE_ALLOC_SIZE(2);
static void _zend_mm_free_int(zend_mm_heap *heap, void *p ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC);
static void *_zend_mm_realloc_int(zend_mm_heap *heap, void *p, size_t size ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC) ZEND_ATTRIBUTE_ALLOC_SIZE(3);

static inline unsigned int zend_mm_high_bit(size_t _size)
{
#if defined(__GNUC__) && (defined(__native_client__) || defined(i386))
	unsigned int n;

	__asm__("bsrl %1,%0\n\t" : "=r" (n) : "rm"  (_size) : "cc");
	return n;
#elif defined(__GNUC__) && defined(__x86_64__)
	unsigned long n;

        __asm__("bsr %1,%0\n\t" : "=r" (n) : "rm"  (_size) : "cc");
        return (unsigned int)n;
#elif defined(_MSC_VER) && defined(_M_IX86)
	__asm {
		bsr eax, _size
	}
#elif defined(__GNUC__) && (defined(__arm__) || defined(__aarch64__) || defined(__powerpc__))
	return (8 * SIZEOF_SIZE_T - 1) - __builtin_clzl(_size);
#else
	unsigned int n = 0;
	while (_size != 0) {
		_size = _size >> 1;
		n++;
	}
	return n-1;
#endif
}

static inline unsigned int zend_mm_low_bit(size_t _size)
{
#if defined(__GNUC__) && (defined(__native_client__) || defined(i386))
	unsigned int n;

	__asm__("bsfl %1,%0\n\t" : "=r" (n) : "rm"  (_size) : "cc");
	return n;
#elif defined(__GNUC__) && defined(__x86_64__)
        unsigned long n;

        __asm__("bsf %1,%0\n\t" : "=r" (n) : "rm"  (_size) : "cc");
        return (unsigned int)n;
#elif defined(_MSC_VER) && defined(_M_IX86)
	__asm {
		bsf eax, _size
   }
#elif defined(__GNUC__) && (defined(__arm__) || defined(__aarch64__) || defined(__powerpc__))
	return __builtin_ctzl(_size);
#else
	static const int offset[16] = {4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0};
	unsigned int n;
	unsigned int index = 0;

	n = offset[_size & 15];
	while (n == 4) {
		_size >>= 4;
		index += n;
		n = offset[_size & 15];
	}

	return index + n;
#endif
}

static inline void zend_mm_add_to_free_list(zend_mm_heap *heap, zend_mm_free_block *mm_block)
{
	size_t size;
	size_t index;

	ZEND_MM_SET_MAGIC(mm_block, MEM_BLOCK_FREED);

	size = ZEND_MM_FREE_BLOCK_SIZE(mm_block);
	if (EXPECTED(!ZEND_MM_SMALL_SIZE(size))) {
		zend_mm_free_block **p;

		index = ZEND_MM_LARGE_BUCKET_INDEX(size);
		p = &heap->large_free_buckets[index];
		mm_block->child[0] = mm_block->child[1] = NULL;
		if (!*p) {
			*p = mm_block;
			mm_block->parent = p;
			mm_block->prev_free_block = mm_block->next_free_block = mm_block;
			heap->large_free_bitmap |= (ZEND_MM_LONG_CONST(1) << index);
		} else {
			size_t m;

			for (m = size << (ZEND_MM_NUM_BUCKETS - index); ; m <<= 1) {
				zend_mm_free_block *prev = *p;

				if (ZEND_MM_FREE_BLOCK_SIZE(prev) != size) {
					p = &prev->child[(m >> (ZEND_MM_NUM_BUCKETS-1)) & 1];
					if (!*p) {
						*p = mm_block;
						mm_block->parent = p;
						mm_block->prev_free_block = mm_block->next_free_block = mm_block;
						break;
					}
				} else {
					zend_mm_free_block *next = prev->next_free_block;

					prev->next_free_block = next->prev_free_block = mm_block;
					mm_block->next_free_block = next;
					mm_block->prev_free_block = prev;
					mm_block->parent = NULL;
					break;
				}
			}
		}
	} else {
		zend_mm_free_block *prev, *next;

		index = ZEND_MM_BUCKET_INDEX(size);

		prev = ZEND_MM_SMALL_FREE_BUCKET(heap, index);
		if (prev->prev_free_block == prev) {
			heap->free_bitmap |= (ZEND_MM_LONG_CONST(1) << index);
		}
		next = prev->next_free_block;

		mm_block->prev_free_block = prev;
		mm_block->next_free_block = next;
		prev->next_free_block = next->prev_free_block = mm_block;
	}
}

static inline void zend_mm_remove_from_free_list(zend_mm_heap *heap, zend_mm_free_block *mm_block)
{
	zend_mm_free_block *prev = mm_block->prev_free_block;
	zend_mm_free_block *next = mm_block->next_free_block;

	ZEND_MM_CHECK_MAGIC(mm_block, MEM_BLOCK_FREED);

	if (EXPECTED(prev == mm_block)) {
		zend_mm_free_block **rp, **cp;

#if ZEND_MM_SAFE_UNLINKING
		if (UNEXPECTED(next != mm_block)) {
			zend_mm_panic("zend_mm_heap corrupted");
		}
#endif

		rp = &mm_block->child[mm_block->child[1] != NULL];
		prev = *rp;
		if (EXPECTED(prev == NULL)) {
			size_t index = ZEND_MM_LARGE_BUCKET_INDEX(ZEND_MM_FREE_BLOCK_SIZE(mm_block));

			ZEND_MM_CHECK_TREE(mm_block);
			*mm_block->parent = NULL;
			if (mm_block->parent == &heap->large_free_buckets[index]) {
				heap->large_free_bitmap &= ~(ZEND_MM_LONG_CONST(1) << index);
		    }
		} else {
			while (*(cp = &(prev->child[prev->child[1] != NULL])) != NULL) {
				prev = *cp;
				rp = cp;
			}
			*rp = NULL;

subst_block:
			ZEND_MM_CHECK_TREE(mm_block);
			*mm_block->parent = prev;
			prev->parent = mm_block->parent;
			if ((prev->child[0] = mm_block->child[0])) {
				ZEND_MM_CHECK_TREE(prev->child[0]);
				prev->child[0]->parent = &prev->child[0];
			}
			if ((prev->child[1] = mm_block->child[1])) {
				ZEND_MM_CHECK_TREE(prev->child[1]);
				prev->child[1]->parent = &prev->child[1];
			}
		}
	} else {

#if ZEND_MM_SAFE_UNLINKING
		if (UNEXPECTED(prev->next_free_block != mm_block) || UNEXPECTED(next->prev_free_block != mm_block)) {
			zend_mm_panic("zend_mm_heap corrupted");
		}
#endif

		prev->next_free_block = next;
		next->prev_free_block = prev;

		if (EXPECTED(ZEND_MM_SMALL_SIZE(ZEND_MM_FREE_BLOCK_SIZE(mm_block)))) {
			if (EXPECTED(prev == next)) {
				size_t index = ZEND_MM_BUCKET_INDEX(ZEND_MM_FREE_BLOCK_SIZE(mm_block));

				if (EXPECTED(heap->free_buckets[index*2] == heap->free_buckets[index*2+1])) {
					heap->free_bitmap &= ~(ZEND_MM_LONG_CONST(1) << index);
				}
			}
		} else if (UNEXPECTED(mm_block->parent == ZEND_MM_REST_BLOCK)) {
			heap->rest_count--;
		} else if (UNEXPECTED(mm_block->parent != NULL)) {
			goto subst_block;
		}
	}
}

static inline void zend_mm_add_to_rest_list(zend_mm_heap *heap, zend_mm_free_block *mm_block)
{
	zend_mm_free_block *prev, *next;

	while (heap->rest_count >= ZEND_MM_MAX_REST_BLOCKS) {
		zend_mm_free_block *p = heap->rest_buckets[1];

		if (!ZEND_MM_SMALL_SIZE(ZEND_MM_FREE_BLOCK_SIZE(p))) {
			heap->rest_count--;
		}
		prev = p->prev_free_block;
		next = p->next_free_block;
		prev->next_free_block = next;
		next->prev_free_block = prev;
		zend_mm_add_to_free_list(heap, p);
	}

	if (!ZEND_MM_SMALL_SIZE(ZEND_MM_FREE_BLOCK_SIZE(mm_block))) {
		mm_block->parent = ZEND_MM_REST_BLOCK;
		heap->rest_count++;
	}

	ZEND_MM_SET_MAGIC(mm_block, MEM_BLOCK_FREED);

	prev = heap->rest_buckets[0];
	next = prev->next_free_block;
	mm_block->prev_free_block = prev;
	mm_block->next_free_block = next;
	prev->next_free_block = next->prev_free_block = mm_block;
}

static inline void zend_mm_init(zend_mm_heap *heap)
{
	zend_mm_free_block* p;
	int i;

	heap->free_bitmap = 0;
	heap->large_free_bitmap = 0;
#if ZEND_MM_CACHE
	heap->cached = 0;
	memset(heap->cache, 0, sizeof(heap->cache));
#endif
#if ZEND_MM_CACHE_STAT
	for (i = 0; i < ZEND_MM_NUM_BUCKETS; i++) {
		heap->cache_stat[i].count = 0;
	}
#endif
	p = ZEND_MM_SMALL_FREE_BUCKET(heap, 0);
	for (i = 0; i < ZEND_MM_NUM_BUCKETS; i++) {
		p->next_free_block = p;
		p->prev_free_block = p;
		p = (zend_mm_free_block*)((char*)p + sizeof(zend_mm_free_block*) * 2);
		heap->large_free_buckets[i] = NULL;
	}
	heap->rest_buckets[0] = heap->rest_buckets[1] = ZEND_MM_REST_BUCKET(heap);
	heap->rest_count = 0;
}

static void zend_mm_del_segment(zend_mm_heap *heap, zend_mm_segment *segment)
{
	zend_mm_segment **p = &heap->segments_list;

	while (*p != segment) {
		p = &(*p)->next_segment;
	}
	*p = segment->next_segment;
	heap->real_size -= segment->size;
	ZEND_MM_STORAGE_FREE(segment);
}

#if ZEND_MM_CACHE
static void zend_mm_free_cache(zend_mm_heap *heap)
{
	int i;

	for (i = 0; i < ZEND_MM_NUM_BUCKETS; i++) {
		if (heap->cache[i]) {
			zend_mm_free_block *mm_block = heap->cache[i];

			while (mm_block) {
				size_t size = ZEND_MM_BLOCK_SIZE(mm_block);
				zend_mm_free_block *q = mm_block->prev_free_block;
				zend_mm_block *next_block = ZEND_MM_NEXT_BLOCK(mm_block);

				heap->cached -= size;

				if (ZEND_MM_PREV_BLOCK_IS_FREE(mm_block)) {
					mm_block = (zend_mm_free_block*)ZEND_MM_PREV_BLOCK(mm_block);
					size += ZEND_MM_FREE_BLOCK_SIZE(mm_block);
					zend_mm_remove_from_free_list(heap, (zend_mm_free_block *) mm_block);
				}
				if (ZEND_MM_IS_FREE_BLOCK(next_block)) {
					size += ZEND_MM_FREE_BLOCK_SIZE(next_block);
					zend_mm_remove_from_free_list(heap, (zend_mm_free_block *) next_block);
				}
				ZEND_MM_BLOCK(mm_block, ZEND_MM_FREE_BLOCK, size);

				if (ZEND_MM_IS_FIRST_BLOCK(mm_block) &&
				    ZEND_MM_IS_GUARD_BLOCK(ZEND_MM_NEXT_BLOCK(mm_block))) {
					zend_mm_del_segment(heap, (zend_mm_segment *) ((char *)mm_block - ZEND_MM_ALIGNED_SEGMENT_SIZE));
				} else {
					zend_mm_add_to_free_list(heap, (zend_mm_free_block *) mm_block);
				}

				mm_block = q;
			}
			heap->cache[i] = NULL;
#if ZEND_MM_CACHE_STAT
			heap->cache_stat[i].count = 0;
#endif
		}
	}
}
#endif

#if ZEND_MM_HEAP_PROTECTION || ZEND_MM_COOKIES
static void zend_mm_random(unsigned char *buf, size_t size) /* {{{ */
{
	size_t i = 0;
	unsigned char t;

#ifdef ZEND_WIN32
	HCRYPTPROV   hCryptProv;
	int has_context = 0;

	if (!CryptAcquireContext(&hCryptProv, NULL, NULL, PROV_RSA_FULL, 0)) {
		/* Could mean that the key container does not exist, let try
		   again by asking for a new one */
		if (GetLastError() == NTE_BAD_KEYSET) {
			if (CryptAcquireContext(&hCryptProv, NULL, NULL, PROV_RSA_FULL, CRYPT_NEWKEYSET)) {
				has_context = 1;
			}
		}
	} else {
		has_context = 1;
	}
	if (has_context) {
		do {
			BOOL ret = CryptGenRandom(hCryptProv, size, buf);
			CryptReleaseContext(hCryptProv, 0);
			if (ret) {
				while (i < size && buf[i] != 0) {
					i++;
				}
				if (i == size) {
					return;
				}
		   }
		} while (0);
	}
#elif defined(HAVE_DEV_URANDOM)
	int fd = open("/dev/urandom", 0);

	if (fd >= 0) {
		if (read(fd, buf, size) == size) {
			while (i < size && buf[i] != 0) {
				i++;
			}
			if (i == size) {
				close(fd);
			    return;
			}
		}
		close(fd);
	}
#endif
	t = (unsigned char)getpid();
	while (i < size) {
		do {
			buf[i] = ((unsigned char)rand()) ^ t;
		} while (buf[i] == 0);
		t = buf[i++] << 1;
    }
}
/* }}} */
#endif

/* Notes:
 * - This function may alter the block_sizes values to match platform alignment
 * - This function does *not* perform sanity checks on the arguments
 */
ZEND_API zend_mm_heap *zend_mm_startup_ex(const zend_mm_mem_handlers *handlers, size_t block_size, size_t reserve_size, int internal, void *params)
{
	zend_mm_storage *storage;
	zend_mm_heap    *heap;

#if 0
	int i;

	printf("ZEND_MM_ALIGNMENT=%d\n", ZEND_MM_ALIGNMENT);
	printf("ZEND_MM_ALIGNMENT_LOG2=%d\n", ZEND_MM_ALIGNMENT_LOG2);
	printf("ZEND_MM_MIN_SIZE=%d\n", ZEND_MM_MIN_SIZE);
	printf("ZEND_MM_MAX_SMALL_SIZE=%d\n", ZEND_MM_MAX_SMALL_SIZE);
	printf("ZEND_MM_ALIGNED_HEADER_SIZE=%d\n", ZEND_MM_ALIGNED_HEADER_SIZE);
	printf("ZEND_MM_ALIGNED_FREE_HEADER_SIZE=%d\n", ZEND_MM_ALIGNED_FREE_HEADER_SIZE);
	printf("ZEND_MM_MIN_ALLOC_BLOCK_SIZE=%d\n", ZEND_MM_MIN_ALLOC_BLOCK_SIZE);
	printf("ZEND_MM_ALIGNED_MIN_HEADER_SIZE=%d\n", ZEND_MM_ALIGNED_MIN_HEADER_SIZE);
	printf("ZEND_MM_ALIGNED_SEGMENT_SIZE=%d\n", ZEND_MM_ALIGNED_SEGMENT_SIZE);
	for (i = 0; i < ZEND_MM_MAX_SMALL_SIZE; i++) {
		printf("%3d%c: %3ld %d %2ld\n", i, (i == ZEND_MM_MIN_SIZE?'*':' '), (long)ZEND_MM_TRUE_SIZE(i), ZEND_MM_SMALL_SIZE(ZEND_MM_TRUE_SIZE(i)), (long)ZEND_MM_BUCKET_INDEX(ZEND_MM_TRUE_SIZE(i)));
	}
	exit(0);
#endif

#if ZEND_MM_HEAP_PROTECTION
	if (_mem_block_start_magic == 0) {
		zend_mm_random((unsigned char*)&_mem_block_start_magic, sizeof(_mem_block_start_magic));
	}
	if (_mem_block_end_magic == 0) {
		zend_mm_random((unsigned char*)&_mem_block_end_magic, sizeof(_mem_block_end_magic));
	}
#endif
#if ZEND_MM_COOKIES
	if (_zend_mm_cookie == 0) {
		zend_mm_random((unsigned char*)&_zend_mm_cookie, sizeof(_zend_mm_cookie));
	}
#endif

	if (zend_mm_low_bit(block_size) != zend_mm_high_bit(block_size)) {
		fprintf(stderr, "'block_size' must be a power of two\n");
/* See http://support.microsoft.com/kb/190351 */
#ifdef PHP_WIN32
		fflush(stderr);
#endif
		exit(255);
	}
	storage = handlers->init(params);
	if (!storage) {
		fprintf(stderr, "Cannot initialize zend_mm storage [%s]\n", handlers->name);
/* See http://support.microsoft.com/kb/190351 */
#ifdef PHP_WIN32
		fflush(stderr);
#endif
		exit(255);
	}
	storage->handlers = handlers;

	heap = malloc(sizeof(struct _zend_mm_heap));
	if (heap == NULL) {
		fprintf(stderr, "Cannot allocate heap for zend_mm storage [%s]\n", handlers->name);
#ifdef PHP_WIN32
		fflush(stderr);
#endif
		exit(255);
	}
	heap->storage = storage;
	heap->block_size = block_size;
	heap->compact_size = 0;
	heap->segments_list = NULL;
	zend_mm_init(heap);
# if ZEND_MM_CACHE_STAT
	memset(heap->cache_stat, 0, sizeof(heap->cache_stat));
# endif

	heap->use_zend_alloc = 1;
	heap->real_size = 0;
	heap->overflow = 0;
	heap->real_peak = 0;
	heap->limit = ZEND_MM_LONG_CONST(1)<<(ZEND_MM_NUM_BUCKETS-2);
	heap->size = 0;
	heap->peak = 0;
	heap->internal = internal;
	heap->reserve = NULL;
	heap->reserve_size = reserve_size;
	if (reserve_size > 0) {
		heap->reserve = _zend_mm_alloc_int(heap, reserve_size ZEND_FILE_LINE_CC ZEND_FILE_LINE_EMPTY_CC);
	}
	if (internal) {
		int i;
		zend_mm_free_block *p, *q, *orig;
		zend_mm_heap *mm_heap = _zend_mm_alloc_int(heap, sizeof(zend_mm_heap)  ZEND_FILE_LINE_CC ZEND_FILE_LINE_EMPTY_CC);

		*mm_heap = *heap;

		p = ZEND_MM_SMALL_FREE_BUCKET(mm_heap, 0);
		orig = ZEND_MM_SMALL_FREE_BUCKET(heap, 0);
		for (i = 0; i < ZEND_MM_NUM_BUCKETS; i++) {
			q = p;
			while (q->prev_free_block != orig) {
				q = q->prev_free_block;
			}
			q->prev_free_block = p;
			q = p;
			while (q->next_free_block != orig) {
				q = q->next_free_block;
			}
			q->next_free_block = p;
			p = (zend_mm_free_block*)((char*)p + sizeof(zend_mm_free_block*) * 2);
			orig = (zend_mm_free_block*)((char*)orig + sizeof(zend_mm_free_block*) * 2);
			if (mm_heap->large_free_buckets[i]) {
				mm_heap->large_free_buckets[i]->parent = &mm_heap->large_free_buckets[i];
			}
		}
		mm_heap->rest_buckets[0] = mm_heap->rest_buckets[1] = ZEND_MM_REST_BUCKET(mm_heap);
		mm_heap->rest_count = 0;

		free(heap);
		heap = mm_heap;
	}
	return heap;
}

ZEND_API zend_mm_heap *zend_mm_startup(void)
{
	int i;
	size_t seg_size;
	char *mem_type = getenv("ZEND_MM_MEM_TYPE");
	char *tmp;
	const zend_mm_mem_handlers *handlers;
	zend_mm_heap *heap;

	if (mem_type == NULL) {
		i = 0;
	} else {
		for (i = 0; mem_handlers[i].name; i++) {
			if (strcmp(mem_handlers[i].name, mem_type) == 0) {
				break;
			}
		}
		if (!mem_handlers[i].name) {
			fprintf(stderr, "Wrong or unsupported zend_mm storage type '%s'\n", mem_type);
			fprintf(stderr, "  supported types:\n");
/* See http://support.microsoft.com/kb/190351 */
#ifdef PHP_WIN32
			fflush(stderr);
#endif
			for (i = 0; mem_handlers[i].name; i++) {
				fprintf(stderr, "    '%s'\n", mem_handlers[i].name);
			}
/* See http://support.microsoft.com/kb/190351 */
#ifdef PHP_WIN32
			fflush(stderr);
#endif
			exit(255);
		}
	}
	handlers = &mem_handlers[i];

	tmp = getenv("ZEND_MM_SEG_SIZE");
	if (tmp) {
		seg_size = zend_atoi(tmp, 0);
		if (zend_mm_low_bit(seg_size) != zend_mm_high_bit(seg_size)) {
			fprintf(stderr, "ZEND_MM_SEG_SIZE must be a power of two\n");
/* See http://support.microsoft.com/kb/190351 */
#ifdef PHP_WIN32
			fflush(stderr);
#endif
			exit(255);
		} else if (seg_size < ZEND_MM_ALIGNED_SEGMENT_SIZE + ZEND_MM_ALIGNED_HEADER_SIZE) {
			fprintf(stderr, "ZEND_MM_SEG_SIZE is too small\n");
/* See http://support.microsoft.com/kb/190351 */
#ifdef PHP_WIN32
			fflush(stderr);
#endif
			exit(255);
		}
	} else {
		seg_size = ZEND_MM_SEG_SIZE;
	}

	heap = zend_mm_startup_ex(handlers, seg_size, ZEND_MM_RESERVE_SIZE, 0, NULL);
	if (heap) {
		tmp = getenv("ZEND_MM_COMPACT");
		if (tmp) {
			heap->compact_size = zend_atoi(tmp, 0);
		} else {
			heap->compact_size = 2 * 1024 * 1024;
		}
	}
	return heap;
}

#if ZEND_DEBUG
static long zend_mm_find_leaks(zend_mm_segment *segment, zend_mm_block *b)
{
	long leaks = 0;
	zend_mm_block *p, *q;

	p = ZEND_MM_NEXT_BLOCK(b);
	while (1) {
		if (ZEND_MM_IS_GUARD_BLOCK(p)) {
			ZEND_MM_CHECK_MAGIC(p, MEM_BLOCK_GUARD);
			segment = segment->next_segment;
			if (!segment) {
				break;
			}
			p = (zend_mm_block *) ((char *) segment + ZEND_MM_ALIGNED_SEGMENT_SIZE);
			continue;
		}
		q = ZEND_MM_NEXT_BLOCK(p);
		if (q <= p ||
		    (char*)q > (char*)segment + segment->size ||
		    p->info._size != q->info._prev) {
		    zend_mm_panic("zend_mm_heap corrupted");
		}
		if (!ZEND_MM_IS_FREE_BLOCK(p)) {
			if (p->magic == MEM_BLOCK_VALID) {
				if (p->debug.filename==b->debug.filename && p->debug.lineno==b->debug.lineno) {
					ZEND_MM_SET_MAGIC(p, MEM_BLOCK_LEAK);
					leaks++;
				}
#if ZEND_MM_CACHE
			} else if (p->magic == MEM_BLOCK_CACHED) {
				/* skip it */
#endif
			} else if (p->magic != MEM_BLOCK_LEAK) {
			    zend_mm_panic("zend_mm_heap corrupted");
			}
		}
		p = q;
	}
	return leaks;
}

static void zend_mm_check_leaks(zend_mm_heap *heap TSRMLS_DC)
{
	zend_mm_segment *segment = heap->segments_list;
	zend_mm_block *p, *q;
	zend_uint total = 0;

	if (!segment) {
		return;
	}
	p = (zend_mm_block *) ((char *) segment + ZEND_MM_ALIGNED_SEGMENT_SIZE);
	while (1) {
		q = ZEND_MM_NEXT_BLOCK(p);
		if (q <= p ||
		    (char*)q > (char*)segment + segment->size ||
		    p->info._size != q->info._prev) {
			zend_mm_panic("zend_mm_heap corrupted");
		}
		if (!ZEND_MM_IS_FREE_BLOCK(p)) {
			if (p->magic == MEM_BLOCK_VALID) {
				long repeated;
				zend_leak_info leak;

				ZEND_MM_SET_MAGIC(p, MEM_BLOCK_LEAK);

				leak.addr = ZEND_MM_DATA_OF(p);
				leak.size = p->debug.size;
				leak.filename = p->debug.filename;
				leak.lineno = p->debug.lineno;
				leak.orig_filename = p->debug.orig_filename;
				leak.orig_lineno = p->debug.orig_lineno;

				zend_message_dispatcher(ZMSG_LOG_SCRIPT_NAME, NULL TSRMLS_CC);
				zend_message_dispatcher(ZMSG_MEMORY_LEAK_DETECTED, &leak TSRMLS_CC);
				repeated = zend_mm_find_leaks(segment, p);
				total += 1 + repeated;
				if (repeated) {
					zend_message_dispatcher(ZMSG_MEMORY_LEAK_REPEATED, (void *)(zend_uintptr_t)repeated TSRMLS_CC);
				}
#if ZEND_MM_CACHE
			} else if (p->magic == MEM_BLOCK_CACHED) {
				/* skip it */
#endif
			} else if (p->magic != MEM_BLOCK_LEAK) {
				zend_mm_panic("zend_mm_heap corrupted");
			}
		}
		if (ZEND_MM_IS_GUARD_BLOCK(q)) {
			segment = segment->next_segment;
			if (!segment) {
				break;
			}
			q = (zend_mm_block *) ((char *) segment + ZEND_MM_ALIGNED_SEGMENT_SIZE);
		}
		p = q;
	}
	if (total) {
		zend_message_dispatcher(ZMSG_MEMORY_LEAKS_GRAND_TOTAL, &total TSRMLS_CC);
	}
}

static int zend_mm_check_ptr(zend_mm_heap *heap, void *ptr, int silent ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC)
{
	zend_mm_block *p;
	int no_cache_notice = 0;
	int had_problems = 0;
	int valid_beginning = 1;

	if (silent==2) {
		silent = 1;
		no_cache_notice = 1;
	} else if (silent==3) {
		silent = 0;
		no_cache_notice = 1;
	}
	if (!silent) {
		TSRMLS_FETCH();

		zend_message_dispatcher(ZMSG_LOG_SCRIPT_NAME, NULL TSRMLS_CC);
		zend_debug_alloc_output("---------------------------------------\n");
		zend_debug_alloc_output("%s(%d) : Block "PTR_FMT" status:\n" ZEND_FILE_LINE_RELAY_CC, ptr);
		if (__zend_orig_filename) {
			zend_debug_alloc_output("%s(%d) : Actual location (location was relayed)\n" ZEND_FILE_LINE_ORIG_RELAY_CC);
		}
		if (!ptr) {
			zend_debug_alloc_output("NULL\n");
			zend_debug_alloc_output("---------------------------------------\n");
			return 0;
		}
	}

	if (!ptr) {
		if (silent) {
			return zend_mm_check_ptr(heap, ptr, 0 ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC);
		}
	}

	p = ZEND_MM_HEADER_OF(ptr);

#ifdef ZTS
	if (ZEND_MM_BAD_THREAD_ID(p)) {
		if (!silent) {
			zend_debug_alloc_output("Invalid pointer: ((thread_id=0x%0.8X) != (expected=0x%0.8X))\n", (long)p->thread_id, (long)tsrm_thread_id());
			had_problems = 1;
		} else {
			return zend_mm_check_ptr(heap, ptr, 0 ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC);
		}
	}
#endif

	if (p->info._size != ZEND_MM_NEXT_BLOCK(p)->info._prev) {
		if (!silent) {
			zend_debug_alloc_output("Invalid pointer: ((size="PTR_FMT") != (next.prev="PTR_FMT"))\n", p->info._size, ZEND_MM_NEXT_BLOCK(p)->info._prev);
			had_problems = 1;
		} else {
			return zend_mm_check_ptr(heap, ptr, 0 ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC);
		}
	}
	if (p->info._prev != ZEND_MM_GUARD_BLOCK &&
	    ZEND_MM_PREV_BLOCK(p)->info._size != p->info._prev) {
		if (!silent) {
			zend_debug_alloc_output("Invalid pointer: ((prev="PTR_FMT") != (prev.size="PTR_FMT"))\n", p->info._prev, ZEND_MM_PREV_BLOCK(p)->info._size);
			had_problems = 1;
		} else {
			return zend_mm_check_ptr(heap, ptr, 0 ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC);
		}
	}

	if (had_problems) {
		zend_debug_alloc_output("---------------------------------------\n");
		return 0;
	}

	if (!silent) {
		zend_debug_alloc_output("%10s\t","Beginning:  ");
	}

	if (!ZEND_MM_IS_USED_BLOCK(p)) {
		if (!silent) {
			if (p->magic != MEM_BLOCK_FREED) {
				zend_debug_alloc_output("Freed (magic=0x%0.8X, expected=0x%0.8X)\n", p->magic, MEM_BLOCK_FREED);
			} else {
				zend_debug_alloc_output("Freed\n");
			}
			had_problems = 1;
		} else {
			return zend_mm_check_ptr(heap, ptr, 0 ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC);
		}
	} else if (ZEND_MM_IS_GUARD_BLOCK(p)) {
		if (!silent) {
			if (p->magic != MEM_BLOCK_FREED) {
				zend_debug_alloc_output("Guard (magic=0x%0.8X, expected=0x%0.8X)\n", p->magic, MEM_BLOCK_FREED);
			} else {
				zend_debug_alloc_output("Guard\n");
			}
			had_problems = 1;
		} else {
			return zend_mm_check_ptr(heap, ptr, 0 ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC);
		}
	} else {
		switch (p->magic) {
			case MEM_BLOCK_VALID:
			case MEM_BLOCK_LEAK:
				if (!silent) {
					zend_debug_alloc_output("OK (allocated on %s:%d, %d bytes)\n", p->debug.filename, p->debug.lineno, (int)p->debug.size);
				}
				break; /* ok */
			case MEM_BLOCK_CACHED:
				if (!no_cache_notice) {
					if (!silent) {
						zend_debug_alloc_output("Cached\n");
						had_problems = 1;
					} else {
						return zend_mm_check_ptr(heap, ptr, 0 ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC);
					}
				}
			case MEM_BLOCK_FREED:
				if (!silent) {
					zend_debug_alloc_output("Freed (invalid)\n");
					had_problems = 1;
				} else {
					return zend_mm_check_ptr(heap, ptr, 0 ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC);
				}
				break;
			case MEM_BLOCK_GUARD:
				if (!silent) {
					zend_debug_alloc_output("Guard (invalid)\n");
					had_problems = 1;
				} else {
					return zend_mm_check_ptr(heap, ptr, 0 ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC);
				}
				break;
			default:
				if (!silent) {
					zend_debug_alloc_output("Unknown (magic=0x%0.8X, expected=0x%0.8X)\n", p->magic, MEM_BLOCK_VALID);
					had_problems = 1;
					valid_beginning = 0;
				} else {
					return zend_mm_check_ptr(heap, ptr, 0 ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC);
				}
				break;
		}
	}

#if ZEND_MM_HEAP_PROTECTION
	if (!valid_beginning) {
		if (!silent) {
			zend_debug_alloc_output("%10s\t", "Start:");
			zend_debug_alloc_output("Unknown\n");
			zend_debug_alloc_output("%10s\t", "End:");
			zend_debug_alloc_output("Unknown\n");
		}
	} else {
		char *end_magic = ZEND_MM_END_MAGIC_PTR(p);

		if (p->debug.start_magic == _mem_block_start_magic) {
			if (!silent) {
				zend_debug_alloc_output("%10s\t", "Start:");
				zend_debug_alloc_output("OK\n");
			}
		} else {
			char *overflow_ptr, *magic_ptr=(char *) &_mem_block_start_magic;
			int overflows=0;
			int i;

			if (silent) {
				return _mem_block_check(ptr, 0 ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC);
			}
			had_problems = 1;
			overflow_ptr = (char *) &p->debug.start_magic;
			i = END_MAGIC_SIZE;
			while (--i >= 0) {
				if (overflow_ptr[i]!=magic_ptr[i]) {
					overflows++;
				}
			}
			zend_debug_alloc_output("%10s\t", "Start:");
			zend_debug_alloc_output("Overflown (magic=0x%0.8X instead of 0x%0.8X)\n", p->debug.start_magic, _mem_block_start_magic);
			zend_debug_alloc_output("%10s\t","");
			if (overflows >= END_MAGIC_SIZE) {
				zend_debug_alloc_output("At least %d bytes overflown\n", END_MAGIC_SIZE);
			} else {
				zend_debug_alloc_output("%d byte(s) overflown\n", overflows);
			}
		}
		if (memcmp(end_magic, &_mem_block_end_magic, END_MAGIC_SIZE)==0) {
			if (!silent) {
				zend_debug_alloc_output("%10s\t", "End:");
				zend_debug_alloc_output("OK\n");
			}
		} else {
			char *overflow_ptr, *magic_ptr=(char *) &_mem_block_end_magic;
			int overflows=0;
			int i;

			if (silent) {
				return _mem_block_check(ptr, 0 ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC);
			}
			had_problems = 1;
			overflow_ptr = (char *) end_magic;

			for (i=0; i < END_MAGIC_SIZE; i++) {
				if (overflow_ptr[i]!=magic_ptr[i]) {
					overflows++;
				}
			}

			zend_debug_alloc_output("%10s\t", "End:");
			zend_debug_alloc_output("Overflown (magic=0x%0.8X instead of 0x%0.8X)\n", *end_magic, _mem_block_end_magic);
			zend_debug_alloc_output("%10s\t","");
			if (overflows >= END_MAGIC_SIZE) {
				zend_debug_alloc_output("At least %d bytes overflown\n", END_MAGIC_SIZE);
			} else {
				zend_debug_alloc_output("%d byte(s) overflown\n", overflows);
			}
		}
	}
#endif

	if (!silent) {
		zend_debug_alloc_output("---------------------------------------\n");
	}
	return ((!had_problems) ? 1 : 0);
}

static int zend_mm_check_heap(zend_mm_heap *heap, int silent ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC)
{
	zend_mm_segment *segment = heap->segments_list;
	zend_mm_block *p, *q;
	int errors = 0;

	if (!segment) {
		return 0;
	}
	p = (zend_mm_block *) ((char *) segment + ZEND_MM_ALIGNED_SEGMENT_SIZE);
	while (1) {
		q = ZEND_MM_NEXT_BLOCK(p);
		if (q <= p ||
		    (char*)q > (char*)segment + segment->size ||
		    p->info._size != q->info._prev) {
			zend_mm_panic("zend_mm_heap corrupted");
		}
		if (!ZEND_MM_IS_FREE_BLOCK(p)) {
			if (p->magic == MEM_BLOCK_VALID || p->magic == MEM_BLOCK_LEAK) {
				if (!zend_mm_check_ptr(heap, ZEND_MM_DATA_OF(p), (silent?2:3) ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC)) {
					errors++;
				}
#if ZEND_MM_CACHE
			} else if (p->magic == MEM_BLOCK_CACHED) {
				/* skip it */
#endif
			} else if (p->magic != MEM_BLOCK_LEAK) {
				zend_mm_panic("zend_mm_heap corrupted");
			}
		}
		if (ZEND_MM_IS_GUARD_BLOCK(q)) {
			segment = segment->next_segment;
			if (!segment) {
				return errors;
			}
			q = (zend_mm_block *) ((char *) segment + ZEND_MM_ALIGNED_SEGMENT_SIZE);
		}
		p = q;
	}
}
#endif

ZEND_API void zend_mm_shutdown(zend_mm_heap *heap, int full_shutdown, int silent TSRMLS_DC)
{
	zend_mm_storage *storage;
	zend_mm_segment *segment;
	zend_mm_segment *prev;
	int internal;

	if (!heap->use_zend_alloc) {
		if (full_shutdown) {
			free(heap);
		}
		return;
	}

	if (heap->reserve) {
#if ZEND_DEBUG
		if (!silent) {
			_zend_mm_free_int(heap, heap->reserve ZEND_FILE_LINE_CC ZEND_FILE_LINE_EMPTY_CC);
		}
#endif
		heap->reserve = NULL;
	}

#if ZEND_MM_CACHE_STAT
	if (full_shutdown) {
		FILE *f;

		f = fopen("zend_mm.log", "w");
		if (f) {
			int i,j;
			size_t size, true_size, min_size, max_size;
			int hit = 0, miss = 0;

			fprintf(f, "\nidx min_size max_size true_size  max_len     hits   misses\n");
			size = 0;
			while (1) {
				true_size = ZEND_MM_TRUE_SIZE(size);
				if (ZEND_MM_SMALL_SIZE(true_size)) {
					min_size = size;
					i = ZEND_MM_BUCKET_INDEX(true_size);
					size++;
					while (1) {
						true_size = ZEND_MM_TRUE_SIZE(size);
						if (ZEND_MM_SMALL_SIZE(true_size)) {
							j = ZEND_MM_BUCKET_INDEX(true_size);
							if (j > i) {
								max_size = size-1;
								break;
							}
						} else {
							max_size = size-1;
							break;
						}
						size++;
					}
					hit += heap->cache_stat[i].hit;
					miss += heap->cache_stat[i].miss;
					fprintf(f, "%2d %8d %8d %9d %8d %8d %8d\n", i, (int)min_size, (int)max_size, ZEND_MM_TRUE_SIZE(max_size), heap->cache_stat[i].max_count, heap->cache_stat[i].hit, heap->cache_stat[i].miss);
				} else {
					break;
				}
			}
			fprintf(f, "                                        %8d %8d\n", hit, miss);
			fprintf(f, "                                        %8d %8d\n", heap->cache_stat[ZEND_MM_NUM_BUCKETS].hit, heap->cache_stat[ZEND_MM_NUM_BUCKETS].miss);
			fclose(f);
		}
	}
#endif

#if ZEND_DEBUG
	if (!silent) {
		zend_mm_check_leaks(heap TSRMLS_CC);
	}
#endif

	internal = heap->internal;
	storage = heap->storage;
	segment = heap->segments_list;
	if (full_shutdown) {
		while (segment) {
			prev = segment;
			segment = segment->next_segment;
			ZEND_MM_STORAGE_FREE(prev);
		}
		heap->segments_list = NULL;
		storage->handlers->dtor(storage);
		if (!internal) {
			free(heap);
		}
	} else {
		if (segment) {
#ifndef ZEND_WIN32
			if (heap->reserve_size) {
				while (segment->next_segment) {
					prev = segment;
					segment = segment->next_segment;
					ZEND_MM_STORAGE_FREE(prev);
				}
				heap->segments_list = segment;
			} else {
#endif
				do {
					prev = segment;
					segment = segment->next_segment;
					ZEND_MM_STORAGE_FREE(prev);
				} while (segment);
				heap->segments_list = NULL;
#ifndef ZEND_WIN32
			}
#endif
		}
		if (heap->compact_size &&
		    heap->real_peak > heap->compact_size) {
			storage->handlers->compact(storage);
		}
		zend_mm_init(heap);
		if (heap->segments_list) {
			heap->real_size = heap->segments_list->size;
			heap->real_peak = heap->segments_list->size;
		} else {
			heap->real_size = 0;
			heap->real_peak = 0;
		}
		heap->size = 0;
		heap->peak = 0;
		if (heap->segments_list) {
			/* mark segment as a free block */
			zend_mm_free_block *b = (zend_mm_free_block*)((char*)heap->segments_list + ZEND_MM_ALIGNED_SEGMENT_SIZE);
			size_t block_size = heap->segments_list->size - ZEND_MM_ALIGNED_SEGMENT_SIZE - ZEND_MM_ALIGNED_HEADER_SIZE;

			ZEND_MM_MARK_FIRST_BLOCK(b);
			ZEND_MM_LAST_BLOCK(ZEND_MM_BLOCK_AT(b, block_size));
			ZEND_MM_BLOCK(b, ZEND_MM_FREE_BLOCK, block_size);
			zend_mm_add_to_free_list(heap, b);
		}
		if (heap->reserve_size) {
			heap->reserve = _zend_mm_alloc_int(heap, heap->reserve_size  ZEND_FILE_LINE_CC ZEND_FILE_LINE_EMPTY_CC);
		}
		heap->overflow = 0;
	}
}

static void zend_mm_safe_error(zend_mm_heap *heap,
	const char *format,
	size_t limit,
#if ZEND_DEBUG
	const char *filename,
	uint lineno,
#endif
	size_t size)
{
	if (heap->reserve) {
		_zend_mm_free_int(heap, heap->reserve ZEND_FILE_LINE_CC ZEND_FILE_LINE_EMPTY_CC);
		heap->reserve = NULL;
	}
	if (heap->overflow == 0) {
		const char *error_filename;
		uint error_lineno;
		TSRMLS_FETCH();
		if (zend_is_compiling(TSRMLS_C)) {
			error_filename = zend_get_compiled_filename(TSRMLS_C);
			error_lineno = zend_get_compiled_lineno(TSRMLS_C);
		} else if (EG(in_execution)) {
			error_filename = EG(active_op_array)?EG(active_op_array)->filename:NULL;
			error_lineno = EG(opline_ptr)?(*EG(opline_ptr))->lineno:0;
		} else {
			error_filename = NULL;
			error_lineno = 0;
		}
		if (!error_filename) {
			error_filename = "Unknown";
		}
		heap->overflow = 1;
		zend_try {
			zend_error_noreturn(E_ERROR,
				format,
				limit,
#if ZEND_DEBUG
				filename,
				lineno,
#endif
				size);
		} zend_catch {
			if (heap->overflow == 2) {
				fprintf(stderr, "\nFatal error: ");
				fprintf(stderr,
					format,
					limit,
#if ZEND_DEBUG
					filename,
					lineno,
#endif
					size);
				fprintf(stderr, " in %s on line %d\n", error_filename, error_lineno);
			}
/* See http://support.microsoft.com/kb/190351 */
#ifdef PHP_WIN32
			fflush(stderr);
#endif
		} zend_end_try();
	} else {
		heap->overflow = 2;
	}
	zend_bailout();
}

static zend_mm_free_block *zend_mm_search_large_block(zend_mm_heap *heap, size_t true_size)
{
	zend_mm_free_block *best_fit;
	size_t index = ZEND_MM_LARGE_BUCKET_INDEX(true_size);
	size_t bitmap = heap->large_free_bitmap >> index;
	zend_mm_free_block *p;

	if (bitmap == 0) {
		return NULL;
	}

	if (UNEXPECTED((bitmap & 1) != 0)) {
		/* Search for best "large" free block */
		zend_mm_free_block *rst = NULL;
		size_t m;
		size_t best_size = -1;

		best_fit = NULL;
		p = heap->large_free_buckets[index];
		for (m = true_size << (ZEND_MM_NUM_BUCKETS - index); ; m <<= 1) {
			if (UNEXPECTED(ZEND_MM_FREE_BLOCK_SIZE(p) == true_size)) {
				return p->next_free_block;
			} else if (ZEND_MM_FREE_BLOCK_SIZE(p) >= true_size &&
			           ZEND_MM_FREE_BLOCK_SIZE(p) < best_size) {
				best_size = ZEND_MM_FREE_BLOCK_SIZE(p);
				best_fit = p;
			}
			if ((m & (ZEND_MM_LONG_CONST(1) << (ZEND_MM_NUM_BUCKETS-1))) == 0) {
				if (p->child[1]) {
					rst = p->child[1];
				}
				if (p->child[0]) {
					p = p->child[0];
				} else {
					break;
				}
			} else if (p->child[1]) {
				p = p->child[1];
			} else {
				break;
			}
		}

		for (p = rst; p; p = p->child[p->child[0] != NULL]) {
			if (UNEXPECTED(ZEND_MM_FREE_BLOCK_SIZE(p) == true_size)) {
				return p->next_free_block;
			} else if (ZEND_MM_FREE_BLOCK_SIZE(p) > true_size &&
			           ZEND_MM_FREE_BLOCK_SIZE(p) < best_size) {
				best_size = ZEND_MM_FREE_BLOCK_SIZE(p);
				best_fit = p;
			}
		}

		if (best_fit) {
			return best_fit->next_free_block;
		}
		bitmap = bitmap >> 1;
		if (!bitmap) {
			return NULL;
		}
		index++;
	}

	/* Search for smallest "large" free block */
	best_fit = p = heap->large_free_buckets[index + zend_mm_low_bit(bitmap)];
	while ((p = p->child[p->child[0] != NULL])) {
		if (ZEND_MM_FREE_BLOCK_SIZE(p) < ZEND_MM_FREE_BLOCK_SIZE(best_fit)) {
			best_fit = p;
		}
	}
	return best_fit->next_free_block;
}

static void *_zend_mm_alloc_int(zend_mm_heap *heap, size_t size ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC)
{
	zend_mm_free_block *best_fit;
	size_t true_size = ZEND_MM_TRUE_SIZE(size);
	size_t block_size;
	size_t remaining_size;
	size_t segment_size;
	zend_mm_segment *segment;
	int keep_rest = 0;
#ifdef ZEND_SIGNALS
	TSRMLS_FETCH();
#endif

	HANDLE_BLOCK_INTERRUPTIONS();

	if (EXPECTED(ZEND_MM_SMALL_SIZE(true_size))) {
		size_t index = ZEND_MM_BUCKET_INDEX(true_size);
		size_t bitmap;

		if (UNEXPECTED(true_size < size)) {
			goto out_of_memory;
		}
#if ZEND_MM_CACHE
		if (EXPECTED(heap->cache[index] != NULL)) {
			/* Get block from cache */
#if ZEND_MM_CACHE_STAT
			heap->cache_stat[index].count--;
			heap->cache_stat[index].hit++;
#endif
			best_fit = heap->cache[index];
			heap->cache[index] = best_fit->prev_free_block;
			heap->cached -= true_size;
			ZEND_MM_CHECK_MAGIC(best_fit, MEM_BLOCK_CACHED);
			ZEND_MM_SET_DEBUG_INFO(best_fit, size, 1, 0);
			HANDLE_UNBLOCK_INTERRUPTIONS();
			return ZEND_MM_DATA_OF(best_fit);
 		}
#if ZEND_MM_CACHE_STAT
		heap->cache_stat[index].miss++;
#endif
#endif

		bitmap = heap->free_bitmap >> index;
		if (bitmap) {
			/* Found some "small" free block that can be used */
			index += zend_mm_low_bit(bitmap);
			best_fit = heap->free_buckets[index*2];
#if ZEND_MM_CACHE_STAT
			heap->cache_stat[ZEND_MM_NUM_BUCKETS].hit++;
#endif
			goto zend_mm_finished_searching_for_block;
		}
	}

#if ZEND_MM_CACHE_STAT
	heap->cache_stat[ZEND_MM_NUM_BUCKETS].miss++;
#endif

	best_fit = zend_mm_search_large_block(heap, true_size);

	if (!best_fit && heap->real_size >= heap->limit - heap->block_size) {
		zend_mm_free_block *p = heap->rest_buckets[0];
		size_t best_size = -1;

		while (p != ZEND_MM_REST_BUCKET(heap)) {
			if (UNEXPECTED(ZEND_MM_FREE_BLOCK_SIZE(p) == true_size)) {
				best_fit = p;
				goto zend_mm_finished_searching_for_block;
			} else if (ZEND_MM_FREE_BLOCK_SIZE(p) > true_size &&
			           ZEND_MM_FREE_BLOCK_SIZE(p) < best_size) {
				best_size = ZEND_MM_FREE_BLOCK_SIZE(p);
				best_fit = p;
			}
			p = p->prev_free_block;
		}
	}

	if (!best_fit) {
		if (true_size > heap->block_size - (ZEND_MM_ALIGNED_SEGMENT_SIZE + ZEND_MM_ALIGNED_HEADER_SIZE)) {
			/* Make sure we add a memory block which is big enough,
			   segment must have header "size" and trailer "guard" block */
			segment_size = true_size + ZEND_MM_ALIGNED_SEGMENT_SIZE + ZEND_MM_ALIGNED_HEADER_SIZE;
			segment_size = (segment_size + (heap->block_size-1)) & ~(heap->block_size-1);
			keep_rest = 1;
		} else {
			segment_size = heap->block_size;
		}

		if (segment_size < true_size ||
		    heap->real_size + segment_size > heap->limit) {
			/* Memory limit overflow */
#if ZEND_MM_CACHE
			zend_mm_free_cache(heap);
#endif
			HANDLE_UNBLOCK_INTERRUPTIONS();
#if ZEND_DEBUG
			zend_mm_safe_error(heap, "Allowed memory size of %ld bytes exhausted at %s:%d (tried to allocate %lu bytes)", heap->limit, __zend_filename, __zend_lineno, size);
#else
			zend_mm_safe_error(heap, "Allowed memory size of %ld bytes exhausted (tried to allocate %lu bytes)", heap->limit, size);
#endif
		}

		segment = (zend_mm_segment *) ZEND_MM_STORAGE_ALLOC(segment_size);

		if (!segment) {
			/* Storage manager cannot allocate memory */
#if ZEND_MM_CACHE
			zend_mm_free_cache(heap);
#endif
out_of_memory:
			HANDLE_UNBLOCK_INTERRUPTIONS();
#if ZEND_DEBUG
			zend_mm_safe_error(heap, "Out of memory (allocated %ld) at %s:%d (tried to allocate %lu bytes)", heap->real_size, __zend_filename, __zend_lineno, size);
#else
			zend_mm_safe_error(heap, "Out of memory (allocated %ld) (tried to allocate %lu bytes)", heap->real_size, size);
#endif
			return NULL;
		}

		heap->real_size += segment_size;
		if (heap->real_size > heap->real_peak) {
			heap->real_peak = heap->real_size;
		}

		segment->size = segment_size;
		segment->next_segment = heap->segments_list;
		heap->segments_list = segment;

		best_fit = (zend_mm_free_block *) ((char *) segment + ZEND_MM_ALIGNED_SEGMENT_SIZE);
		ZEND_MM_MARK_FIRST_BLOCK(best_fit);

		block_size = segment_size - ZEND_MM_ALIGNED_SEGMENT_SIZE - ZEND_MM_ALIGNED_HEADER_SIZE;

		ZEND_MM_LAST_BLOCK(ZEND_MM_BLOCK_AT(best_fit, block_size));

	} else {
zend_mm_finished_searching_for_block:
		/* remove from free list */
		ZEND_MM_CHECK_MAGIC(best_fit, MEM_BLOCK_FREED);
		ZEND_MM_CHECK_COOKIE(best_fit);
		ZEND_MM_CHECK_BLOCK_LINKAGE(best_fit);
		zend_mm_remove_from_free_list(heap, best_fit);

		block_size = ZEND_MM_FREE_BLOCK_SIZE(best_fit);
	}

	remaining_size = block_size - true_size;

	if (remaining_size < ZEND_MM_ALIGNED_MIN_HEADER_SIZE) {
		true_size = block_size;
		ZEND_MM_BLOCK(best_fit, ZEND_MM_USED_BLOCK, true_size);
	} else {
		zend_mm_free_block *new_free_block;

		/* prepare new free block */
		ZEND_MM_BLOCK(best_fit, ZEND_MM_USED_BLOCK, true_size);
		new_free_block = (zend_mm_free_block *) ZEND_MM_BLOCK_AT(best_fit, true_size);
		ZEND_MM_BLOCK(new_free_block, ZEND_MM_FREE_BLOCK, remaining_size);

		/* add the new free block to the free list */
		if (EXPECTED(!keep_rest)) {
			zend_mm_add_to_free_list(heap, new_free_block);
		} else {
			zend_mm_add_to_rest_list(heap, new_free_block);
		}
	}

	ZEND_MM_SET_DEBUG_INFO(best_fit, size, 1, 1);

	heap->size += true_size;
	if (heap->peak < heap->size) {
		heap->peak = heap->size;
	}

	HANDLE_UNBLOCK_INTERRUPTIONS();

	return ZEND_MM_DATA_OF(best_fit);
}


static void _zend_mm_free_int(zend_mm_heap *heap, void *p ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC)
{
	zend_mm_block *mm_block;
	zend_mm_block *next_block;
	size_t size;
#ifdef ZEND_SIGNALS
	TSRMLS_FETCH();
#endif
	if (!ZEND_MM_VALID_PTR(p)) {
		return;
	}

	HANDLE_BLOCK_INTERRUPTIONS();

	mm_block = ZEND_MM_HEADER_OF(p);
	size = ZEND_MM_BLOCK_SIZE(mm_block);
	ZEND_MM_CHECK_PROTECTION(mm_block);

#if ZEND_DEBUG || ZEND_MM_HEAP_PROTECTION
	memset(ZEND_MM_DATA_OF(mm_block), 0x5a, mm_block->debug.size);
#endif

#if ZEND_MM_CACHE
	if (EXPECTED(ZEND_MM_SMALL_SIZE(size)) && EXPECTED(heap->cached < ZEND_MM_CACHE_SIZE)) {
		size_t index = ZEND_MM_BUCKET_INDEX(size);
		zend_mm_free_block **cache = &heap->cache[index];

		((zend_mm_free_block*)mm_block)->prev_free_block = *cache;
		*cache = (zend_mm_free_block*)mm_block;
		heap->cached += size;
		ZEND_MM_SET_MAGIC(mm_block, MEM_BLOCK_CACHED);
#if ZEND_MM_CACHE_STAT
		if (++heap->cache_stat[index].count > heap->cache_stat[index].max_count) {
			heap->cache_stat[index].max_count = heap->cache_stat[index].count;
		}
#endif
		HANDLE_UNBLOCK_INTERRUPTIONS();
		return;
	}
#endif

	heap->size -= size;

	next_block = ZEND_MM_BLOCK_AT(mm_block, size);
	if (ZEND_MM_IS_FREE_BLOCK(next_block)) {
		zend_mm_remove_from_free_list(heap, (zend_mm_free_block *) next_block);
		size += ZEND_MM_FREE_BLOCK_SIZE(next_block);
	}
	if (ZEND_MM_PREV_BLOCK_IS_FREE(mm_block)) {
		mm_block = ZEND_MM_PREV_BLOCK(mm_block);
		zend_mm_remove_from_free_list(heap, (zend_mm_free_block *) mm_block);
		size += ZEND_MM_FREE_BLOCK_SIZE(mm_block);
	}
	if (ZEND_MM_IS_FIRST_BLOCK(mm_block) &&
	    ZEND_MM_IS_GUARD_BLOCK(ZEND_MM_BLOCK_AT(mm_block, size))) {
		zend_mm_del_segment(heap, (zend_mm_segment *) ((char *)mm_block - ZEND_MM_ALIGNED_SEGMENT_SIZE));
	} else {
		ZEND_MM_BLOCK(mm_block, ZEND_MM_FREE_BLOCK, size);
		zend_mm_add_to_free_list(heap, (zend_mm_free_block *) mm_block);
	}
	HANDLE_UNBLOCK_INTERRUPTIONS();
}

static void *_zend_mm_realloc_int(zend_mm_heap *heap, void *p, size_t size ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC)
{
	zend_mm_block *mm_block = ZEND_MM_HEADER_OF(p);
	zend_mm_block *next_block;
	size_t true_size;
	size_t orig_size;
	void *ptr;
#ifdef ZEND_SIGNALS
	TSRMLS_FETCH();
#endif
	if (UNEXPECTED(!p) || !ZEND_MM_VALID_PTR(p)) {
		return _zend_mm_alloc_int(heap, size ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC);
	}

	HANDLE_BLOCK_INTERRUPTIONS();

	mm_block = ZEND_MM_HEADER_OF(p);
	true_size = ZEND_MM_TRUE_SIZE(size);
	orig_size = ZEND_MM_BLOCK_SIZE(mm_block);
	ZEND_MM_CHECK_PROTECTION(mm_block);

	if (UNEXPECTED(true_size < size)) {
		goto out_of_memory;
	}

	if (true_size <= orig_size) {
		size_t remaining_size = orig_size - true_size;

		if (remaining_size >= ZEND_MM_ALIGNED_MIN_HEADER_SIZE) {
			zend_mm_free_block *new_free_block;

			next_block = ZEND_MM_BLOCK_AT(mm_block, orig_size);
			if (ZEND_MM_IS_FREE_BLOCK(next_block)) {
				remaining_size += ZEND_MM_FREE_BLOCK_SIZE(next_block);
				zend_mm_remove_from_free_list(heap, (zend_mm_free_block *) next_block);
			}

			/* prepare new free block */
			ZEND_MM_BLOCK(mm_block, ZEND_MM_USED_BLOCK, true_size);
			new_free_block = (zend_mm_free_block *) ZEND_MM_BLOCK_AT(mm_block, true_size);

			ZEND_MM_BLOCK(new_free_block, ZEND_MM_FREE_BLOCK, remaining_size);

			/* add the new free block to the free list */
			zend_mm_add_to_free_list(heap, new_free_block);
			heap->size += (true_size - orig_size);
		}
		ZEND_MM_SET_DEBUG_INFO(mm_block, size, 0, 0);
		HANDLE_UNBLOCK_INTERRUPTIONS();
		return p;
	}

#if ZEND_MM_CACHE
	if (ZEND_MM_SMALL_SIZE(true_size)) {
		size_t index = ZEND_MM_BUCKET_INDEX(true_size);

		if (heap->cache[index] != NULL) {
			zend_mm_free_block *best_fit;
			zend_mm_free_block **cache;

#if ZEND_MM_CACHE_STAT
			heap->cache_stat[index].count--;
			heap->cache_stat[index].hit++;
#endif
			best_fit = heap->cache[index];
			heap->cache[index] = best_fit->prev_free_block;
			ZEND_MM_CHECK_MAGIC(best_fit, MEM_BLOCK_CACHED);
			ZEND_MM_SET_DEBUG_INFO(best_fit, size, 1, 0);

			ptr = ZEND_MM_DATA_OF(best_fit);

#if ZEND_DEBUG || ZEND_MM_HEAP_PROTECTION
			memcpy(ptr, p, mm_block->debug.size);
#else
			memcpy(ptr, p, orig_size - ZEND_MM_ALIGNED_HEADER_SIZE);
#endif

			heap->cached -= true_size - orig_size;

			index = ZEND_MM_BUCKET_INDEX(orig_size);
			cache = &heap->cache[index];

			((zend_mm_free_block*)mm_block)->prev_free_block = *cache;
			*cache = (zend_mm_free_block*)mm_block;
			ZEND_MM_SET_MAGIC(mm_block, MEM_BLOCK_CACHED);
#if ZEND_MM_CACHE_STAT
			if (++heap->cache_stat[index].count > heap->cache_stat[index].max_count) {
				heap->cache_stat[index].max_count = heap->cache_stat[index].count;
			}
#endif

			HANDLE_UNBLOCK_INTERRUPTIONS();
			return ptr;
		}
	}
#endif

	next_block = ZEND_MM_BLOCK_AT(mm_block, orig_size);

	if (ZEND_MM_IS_FREE_BLOCK(next_block)) {
		ZEND_MM_CHECK_COOKIE(next_block);
		ZEND_MM_CHECK_BLOCK_LINKAGE(next_block);
		if (orig_size + ZEND_MM_FREE_BLOCK_SIZE(next_block) >= true_size) {
			size_t block_size = orig_size + ZEND_MM_FREE_BLOCK_SIZE(next_block);
			size_t remaining_size = block_size - true_size;

			zend_mm_remove_from_free_list(heap, (zend_mm_free_block *) next_block);

			if (remaining_size < ZEND_MM_ALIGNED_MIN_HEADER_SIZE) {
				true_size = block_size;
				ZEND_MM_BLOCK(mm_block, ZEND_MM_USED_BLOCK, true_size);
			} else {
				zend_mm_free_block *new_free_block;

				/* prepare new free block */
				ZEND_MM_BLOCK(mm_block, ZEND_MM_USED_BLOCK, true_size);
				new_free_block = (zend_mm_free_block *) ZEND_MM_BLOCK_AT(mm_block, true_size);
				ZEND_MM_BLOCK(new_free_block, ZEND_MM_FREE_BLOCK, remaining_size);

				/* add the new free block to the free list */
				if (ZEND_MM_IS_FIRST_BLOCK(mm_block) &&
				    ZEND_MM_IS_GUARD_BLOCK(ZEND_MM_BLOCK_AT(new_free_block, remaining_size))) {
					zend_mm_add_to_rest_list(heap, new_free_block);
				} else {
					zend_mm_add_to_free_list(heap, new_free_block);
				}
			}
			ZEND_MM_SET_DEBUG_INFO(mm_block, size, 0, 0);
			heap->size = heap->size + true_size - orig_size;
			if (heap->peak < heap->size) {
				heap->peak = heap->size;
			}
			HANDLE_UNBLOCK_INTERRUPTIONS();
			return p;
		} else if (ZEND_MM_IS_FIRST_BLOCK(mm_block) &&
				   ZEND_MM_IS_GUARD_BLOCK(ZEND_MM_BLOCK_AT(next_block, ZEND_MM_FREE_BLOCK_SIZE(next_block)))) {
			zend_mm_remove_from_free_list(heap, (zend_mm_free_block *) next_block);
			goto realloc_segment;
		}
	} else if (ZEND_MM_IS_FIRST_BLOCK(mm_block) && ZEND_MM_IS_GUARD_BLOCK(next_block)) {
		zend_mm_segment *segment;
		zend_mm_segment *segment_copy;
		size_t segment_size;
		size_t block_size;
		size_t remaining_size;

realloc_segment:
		/* segment size, size of block and size of guard block */
		if (true_size > heap->block_size - (ZEND_MM_ALIGNED_SEGMENT_SIZE + ZEND_MM_ALIGNED_HEADER_SIZE)) {
			segment_size = true_size+ZEND_MM_ALIGNED_SEGMENT_SIZE+ZEND_MM_ALIGNED_HEADER_SIZE;
			segment_size = (segment_size + (heap->block_size-1)) & ~(heap->block_size-1);
		} else {
			segment_size = heap->block_size;
		}

		segment_copy = (zend_mm_segment *) ((char *)mm_block - ZEND_MM_ALIGNED_SEGMENT_SIZE);
		if (segment_size < true_size ||
		    heap->real_size + segment_size - segment_copy->size > heap->limit) {
			if (ZEND_MM_IS_FREE_BLOCK(next_block)) {
				zend_mm_add_to_free_list(heap, (zend_mm_free_block *) next_block);
			}
#if ZEND_MM_CACHE
			zend_mm_free_cache(heap);
#endif
			HANDLE_UNBLOCK_INTERRUPTIONS();
#if ZEND_DEBUG
			zend_mm_safe_error(heap, "Allowed memory size of %ld bytes exhausted at %s:%d (tried to allocate %ld bytes)", heap->limit, __zend_filename, __zend_lineno, size);
#else
			zend_mm_safe_error(heap, "Allowed memory size of %ld bytes exhausted (tried to allocate %ld bytes)", heap->limit, size);
#endif
			return NULL;
		}

		segment = ZEND_MM_STORAGE_REALLOC(segment_copy, segment_size);
		if (!segment) {
#if ZEND_MM_CACHE
			zend_mm_free_cache(heap);
#endif
out_of_memory:
			HANDLE_UNBLOCK_INTERRUPTIONS();
#if ZEND_DEBUG
			zend_mm_safe_error(heap, "Out of memory (allocated %ld) at %s:%d (tried to allocate %ld bytes)", heap->real_size, __zend_filename, __zend_lineno, size);
#else
			zend_mm_safe_error(heap, "Out of memory (allocated %ld) (tried to allocate %ld bytes)", heap->real_size, size);
#endif
			return NULL;
		}
		heap->real_size += segment_size - segment->size;
		if (heap->real_size > heap->real_peak) {
			heap->real_peak = heap->real_size;
		}

		segment->size = segment_size;

		if (segment != segment_copy) {
			zend_mm_segment **seg = &heap->segments_list;
			while (*seg != segment_copy) {
				seg = &(*seg)->next_segment;
			}
			*seg = segment;
			mm_block = (zend_mm_block *) ((char *) segment + ZEND_MM_ALIGNED_SEGMENT_SIZE);
			ZEND_MM_MARK_FIRST_BLOCK(mm_block);
		}

		block_size = segment_size - ZEND_MM_ALIGNED_SEGMENT_SIZE - ZEND_MM_ALIGNED_HEADER_SIZE;
		remaining_size = block_size - true_size;

		/* setup guard block */
		ZEND_MM_LAST_BLOCK(ZEND_MM_BLOCK_AT(mm_block, block_size));

		if (remaining_size < ZEND_MM_ALIGNED_MIN_HEADER_SIZE) {
			true_size = block_size;
			ZEND_MM_BLOCK(mm_block, ZEND_MM_USED_BLOCK, true_size);
		} else {
			zend_mm_free_block *new_free_block;

			/* prepare new free block */
			ZEND_MM_BLOCK(mm_block, ZEND_MM_USED_BLOCK, true_size);
			new_free_block = (zend_mm_free_block *) ZEND_MM_BLOCK_AT(mm_block, true_size);
			ZEND_MM_BLOCK(new_free_block, ZEND_MM_FREE_BLOCK, remaining_size);

			/* add the new free block to the free list */
			zend_mm_add_to_rest_list(heap, new_free_block);
		}

		ZEND_MM_SET_DEBUG_INFO(mm_block, size, 1, 1);

		heap->size = heap->size + true_size - orig_size;
		if (heap->peak < heap->size) {
			heap->peak = heap->size;
		}

		HANDLE_UNBLOCK_INTERRUPTIONS();
		return ZEND_MM_DATA_OF(mm_block);
	}

	ptr = _zend_mm_alloc_int(heap, size ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC);
#if ZEND_DEBUG || ZEND_MM_HEAP_PROTECTION
	memcpy(ptr, p, mm_block->debug.size);
#else
	memcpy(ptr, p, orig_size - ZEND_MM_ALIGNED_HEADER_SIZE);
#endif
	_zend_mm_free_int(heap, p ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC);
	HANDLE_UNBLOCK_INTERRUPTIONS();
	return ptr;
}

ZEND_API void *_zend_mm_alloc(zend_mm_heap *heap, size_t size ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC)
{
	return _zend_mm_alloc_int(heap, size ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC);
}

ZEND_API void _zend_mm_free(zend_mm_heap *heap, void *p ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC)
{
	_zend_mm_free_int(heap, p ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC);
}

ZEND_API void *_zend_mm_realloc(zend_mm_heap *heap, void *ptr, size_t size ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC)
{
	return _zend_mm_realloc_int(heap, ptr, size ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC);
}

ZEND_API size_t _zend_mm_block_size(zend_mm_heap *heap, void *p ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC)
{
	zend_mm_block *mm_block;

	if (!ZEND_MM_VALID_PTR(p)) {
		return 0;
	}
	mm_block = ZEND_MM_HEADER_OF(p);
	ZEND_MM_CHECK_PROTECTION(mm_block);
#if ZEND_DEBUG || ZEND_MM_HEAP_PROTECTION
	return mm_block->debug.size;
#else
	return ZEND_MM_BLOCK_SIZE(mm_block);
#endif
}

/**********************/
/* Allocation Manager */
/**********************/

typedef struct _zend_alloc_globals {
	zend_mm_heap *mm_heap;
} zend_alloc_globals;

#ifdef ZTS
static int alloc_globals_id;
# define AG(v) TSRMG(alloc_globals_id, zend_alloc_globals *, v)
#else
# define AG(v) (alloc_globals.v)
static zend_alloc_globals alloc_globals;
#endif

ZEND_API int is_zend_mm(TSRMLS_D)
{
	return AG(mm_heap)->use_zend_alloc;
}

ZEND_API void *_emalloc(size_t size ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC)
{
	TSRMLS_FETCH();

	if (UNEXPECTED(!AG(mm_heap)->use_zend_alloc)) {
		return AG(mm_heap)->_malloc(size);
	}
	return _zend_mm_alloc_int(AG(mm_heap), size ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC);
}

ZEND_API void _efree(void *ptr ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC)
{
	TSRMLS_FETCH();

	if (UNEXPECTED(!AG(mm_heap)->use_zend_alloc)) {
		AG(mm_heap)->_free(ptr);
		return;
	}
	_zend_mm_free_int(AG(mm_heap), ptr ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC);
}

ZEND_API void *_erealloc(void *ptr, size_t size, int allow_failure ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC)
{
	TSRMLS_FETCH();

	if (UNEXPECTED(!AG(mm_heap)->use_zend_alloc)) {
		return AG(mm_heap)->_realloc(ptr, size);
	}
	return _zend_mm_realloc_int(AG(mm_heap), ptr, size ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC);
}

ZEND_API size_t _zend_mem_block_size(void *ptr TSRMLS_DC ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC)
{
	if (UNEXPECTED(!AG(mm_heap)->use_zend_alloc)) {
		return 0;
	}
	return _zend_mm_block_size(AG(mm_heap), ptr ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC);
}

#if defined(__GNUC__) && (defined(__native_client__) || defined(i386))

static inline size_t safe_address(size_t nmemb, size_t size, size_t offset)
{
	size_t res = nmemb;
	unsigned long overflow = 0;

	__asm__ ("mull %3\n\taddl %4,%0\n\tadcl $0,%1"
	     : "=&a"(res), "=&d" (overflow)
	     : "%0"(res),
	       "rm"(size),
	       "rm"(offset));

	if (UNEXPECTED(overflow)) {
		zend_error_noreturn(E_ERROR, "Possible integer overflow in memory allocation (%zu * %zu + %zu)", nmemb, size, offset);
		return 0;
	}
	return res;
}

#elif defined(__GNUC__) && defined(__x86_64__)

static inline size_t safe_address(size_t nmemb, size_t size, size_t offset)
{
        size_t res = nmemb;
        unsigned long overflow = 0;

#ifdef __ILP32__ /* x32 */
# define LP_SUFF "l"
#else /* amd64 */
# define LP_SUFF "q"
#endif

        __asm__ ("mul" LP_SUFF  " %3\n\t"
                 "add %4,%0\n\t"
                 "adc $0,%1"
             : "=&a"(res), "=&d" (overflow)
             : "%0"(res),
               "rm"(size),
               "rm"(offset));

#undef LP_SUFF
        if (UNEXPECTED(overflow)) {
                zend_error_noreturn(E_ERROR, "Possible integer overflow in memory allocation (%zu * %zu + %zu)", nmemb, size, offset);
                return 0;
        }
        return res;
}

#elif defined(__GNUC__) && defined(__arm__)

static inline size_t safe_address(size_t nmemb, size_t size, size_t offset)
{
        size_t res;
        unsigned long overflow;

        __asm__ ("umlal %0,%1,%2,%3"
             : "=r"(res), "=r"(overflow)
             : "r"(nmemb),
               "r"(size),
               "0"(offset),
               "1"(0));

        if (UNEXPECTED(overflow)) {
                zend_error_noreturn(E_ERROR, "Possible integer overflow in memory allocation (%zu * %zu + %zu)", nmemb, size, offset);
                return 0;
        }
        return res;
}

#elif defined(__GNUC__) && defined(__aarch64__)

static inline size_t safe_address(size_t nmemb, size_t size, size_t offset)
{
        size_t res;
        unsigned long overflow;

        __asm__ ("mul %0,%2,%3\n\tumulh %1,%2,%3\n\tadds %0,%0,%4\n\tadc %1,%1,xzr"
             : "=&r"(res), "=&r"(overflow)
             : "r"(nmemb),
               "r"(size),
               "r"(offset));

        if (UNEXPECTED(overflow)) {
                zend_error_noreturn(E_ERROR, "Possible integer overflow in memory allocation (%zu * %zu + %zu)", nmemb, size, offset);
                return 0;
        }
        return res;
}

#elif SIZEOF_SIZE_T == 4 && defined(HAVE_ZEND_LONG64)

static inline size_t safe_address(size_t nmemb, size_t size, size_t offset)
{
	zend_ulong64 res = (zend_ulong64)nmemb * (zend_ulong64)size + (zend_ulong64)offset;

	if (UNEXPECTED(res > (zend_ulong64)0xFFFFFFFFL)) {
		zend_error_noreturn(E_ERROR, "Possible integer overflow in memory allocation (%zu * %zu + %zu)", nmemb, size, offset);
		return 0;
	}
	return (size_t) res;
}

#else

static inline size_t safe_address(size_t nmemb, size_t size, size_t offset)
{
	size_t res = nmemb * size + offset;
	double _d  = (double)nmemb * (double)size + (double)offset;
	double _delta = (double)res - _d;

	if (UNEXPECTED((_d + _delta ) != _d)) {
		zend_error_noreturn(E_ERROR, "Possible integer overflow in memory allocation (%zu * %zu + %zu)", nmemb, size, offset);
		return 0;
	}
	return res;
}
#endif


ZEND_API void *_safe_emalloc(size_t nmemb, size_t size, size_t offset ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC)
{
	return emalloc_rel(safe_address(nmemb, size, offset));
}

ZEND_API void *_safe_malloc(size_t nmemb, size_t size, size_t offset)
{
	return pemalloc(safe_address(nmemb, size, offset), 1);
}

ZEND_API void *_safe_erealloc(void *ptr, size_t nmemb, size_t size, size_t offset ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC)
{
	return erealloc_rel(ptr, safe_address(nmemb, size, offset));
}

ZEND_API void *_safe_realloc(void *ptr, size_t nmemb, size_t size, size_t offset)
{
	return perealloc(ptr, safe_address(nmemb, size, offset), 1);
}


ZEND_API void *_ecalloc(size_t nmemb, size_t size ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC)
{
	void *p;
#ifdef ZEND_SIGNALS
	TSRMLS_FETCH();
#endif
	HANDLE_BLOCK_INTERRUPTIONS();

	p = _safe_emalloc(nmemb, size, 0 ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC);
	if (UNEXPECTED(p == NULL)) {
		HANDLE_UNBLOCK_INTERRUPTIONS();
		return p;
	}
	memset(p, 0, size * nmemb);
	HANDLE_UNBLOCK_INTERRUPTIONS();
	return p;
}

ZEND_API char *_estrdup(const char *s ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC)
{
	size_t length;
	char *p;
#ifdef ZEND_SIGNALS
	TSRMLS_FETCH();
#endif

	HANDLE_BLOCK_INTERRUPTIONS();

	length = strlen(s);
	p = (char *) _emalloc(safe_address(length, 1, 1) ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC);
	if (UNEXPECTED(p == NULL)) {
		HANDLE_UNBLOCK_INTERRUPTIONS();
		return p;
	}
	memcpy(p, s, length+1);
	HANDLE_UNBLOCK_INTERRUPTIONS();
	return p;
}

ZEND_API char *_estrndup(const char *s, uint length ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC)
{
	char *p;
#ifdef ZEND_SIGNALS
	TSRMLS_FETCH();
#endif

	HANDLE_BLOCK_INTERRUPTIONS();

	p = (char *) _emalloc(safe_address(length, 1, 1) ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC);
	if (UNEXPECTED(p == NULL)) {
		HANDLE_UNBLOCK_INTERRUPTIONS();
		return p;
	}
	memcpy(p, s, length);
	p[length] = 0;
	HANDLE_UNBLOCK_INTERRUPTIONS();
	return p;
}


ZEND_API char *zend_strndup(const char *s, uint length)
{
	char *p;
#ifdef ZEND_SIGNALS
	TSRMLS_FETCH();
#endif

	HANDLE_BLOCK_INTERRUPTIONS();

	p = (char *) malloc(safe_address(length, 1, 1));
	if (UNEXPECTED(p == NULL)) {
		HANDLE_UNBLOCK_INTERRUPTIONS();
		return p;
	}
	if (length) {
		memcpy(p, s, length);
	}
	p[length] = 0;
	HANDLE_UNBLOCK_INTERRUPTIONS();
	return p;
}


ZEND_API int zend_set_memory_limit(size_t memory_limit)
{
	TSRMLS_FETCH();

	AG(mm_heap)->limit = (memory_limit >= AG(mm_heap)->block_size) ? memory_limit : AG(mm_heap)->block_size;

	return SUCCESS;
}

ZEND_API size_t zend_memory_usage(int real_usage TSRMLS_DC)
{
	if (real_usage) {
		return AG(mm_heap)->real_size;
	} else {
		size_t usage = AG(mm_heap)->size;
#if ZEND_MM_CACHE
		usage -= AG(mm_heap)->cached;
#endif
		return usage;
	}
}

ZEND_API size_t zend_memory_peak_usage(int real_usage TSRMLS_DC)
{
	if (real_usage) {
		return AG(mm_heap)->real_peak;
	} else {
		return AG(mm_heap)->peak;
	}
}

ZEND_API void shutdown_memory_manager(int silent, int full_shutdown TSRMLS_DC)
{
	zend_mm_shutdown(AG(mm_heap), full_shutdown, silent TSRMLS_CC);
}

static void alloc_globals_ctor(zend_alloc_globals *alloc_globals TSRMLS_DC)
{
	char *tmp = getenv("USE_ZEND_ALLOC");

	if (tmp && !zend_atoi(tmp, 0)) {
		alloc_globals->mm_heap = malloc(sizeof(struct _zend_mm_heap));
		memset(alloc_globals->mm_heap, 0, sizeof(struct _zend_mm_heap));
		alloc_globals->mm_heap->use_zend_alloc = 0;
		alloc_globals->mm_heap->_malloc = malloc;
		alloc_globals->mm_heap->_free = free;
		alloc_globals->mm_heap->_realloc = realloc;
	} else {
		alloc_globals->mm_heap = zend_mm_startup();
	}
}

#ifdef ZTS
static void alloc_globals_dtor(zend_alloc_globals *alloc_globals TSRMLS_DC)
{
	shutdown_memory_manager(1, 1 TSRMLS_CC);
}
#endif

ZEND_API void start_memory_manager(TSRMLS_D)
{
#ifdef ZTS
	ts_allocate_id(&alloc_globals_id, sizeof(zend_alloc_globals), (ts_allocate_ctor) alloc_globals_ctor, (ts_allocate_dtor) alloc_globals_dtor);
#else
	alloc_globals_ctor(&alloc_globals);
#endif
}

ZEND_API zend_mm_heap *zend_mm_set_heap(zend_mm_heap *new_heap TSRMLS_DC)
{
	zend_mm_heap *old_heap;

	old_heap = AG(mm_heap);
	AG(mm_heap) = new_heap;
	return old_heap;
}

ZEND_API zend_mm_storage *zend_mm_get_storage(zend_mm_heap *heap)
{
	return heap->storage;
}

ZEND_API void zend_mm_set_custom_handlers(zend_mm_heap *heap,
                                          void* (*_malloc)(size_t),
                                          void  (*_free)(void*),
                                          void* (*_realloc)(void*, size_t))
{
	heap->use_zend_alloc = 0;
	heap->_malloc = _malloc;
	heap->_free = _free;
	heap->_realloc = _realloc;
}

#if ZEND_DEBUG
ZEND_API int _mem_block_check(void *ptr, int silent ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC)
{
	TSRMLS_FETCH();

	if (!AG(mm_heap)->use_zend_alloc) {
		return 1;
	}
	return zend_mm_check_ptr(AG(mm_heap), ptr, silent ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC);
}


ZEND_API void _full_mem_check(int silent ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC)
{
	int errors;
	TSRMLS_FETCH();

	if (!AG(mm_heap)->use_zend_alloc) {
		return;
	}

	zend_debug_alloc_output("------------------------------------------------\n");
	zend_debug_alloc_output("Full Memory Check at %s:%d\n" ZEND_FILE_LINE_RELAY_CC);

	errors = zend_mm_check_heap(AG(mm_heap), silent ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC);

	zend_debug_alloc_output("End of full memory check %s:%d (%d errors)\n" ZEND_FILE_LINE_RELAY_CC, errors);
	zend_debug_alloc_output("------------------------------------------------\n");
}
#endif

/*
 * Local variables:
 * tab-width: 4
 * c-basic-offset: 4
 * indent-tabs-mode: t
 * End:
 */


<|start_filename|>Zend/zend_istdiostream.h<|end_filename|>
/* 
   +----------------------------------------------------------------------+
   | Zend Engine                                                          |
   +----------------------------------------------------------------------+
   | Copyright (c) 1998-2015 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          |
   | <EMAIL> so we can mail you a copy immediately.              |
   +----------------------------------------------------------------------+
   | Authors: <NAME> <<EMAIL>>                                |
   |          <NAME> <<EMAIL>>                                |
   +----------------------------------------------------------------------+
*/

/* $Id$ */

#ifndef _ZEND_STDIOSTREAM
#define _ZEND_STDIOSTREAM

#if defined(ZTS) && !defined(HAVE_CLASS_ISTDIOSTREAM)
class istdiostream : public istream
{
private:
	stdiobuf _file;
public:
	istdiostream (FILE* __f) : istream(), _file(__f) { init(&_file); }
	stdiobuf* rdbuf()/* const */ { return &_file; }
};
#endif

#endif

/*
 * Local variables:
 * tab-width: 4
 * c-basic-offset: 4
 * indent-tabs-mode: t
 * End:
 */


<|start_filename|>ext/pcre/pcrelib/sljit/sljitNativeSPARC_common.c<|end_filename|>
/*
 *    Stack-less Just-In-Time compiler
 *
 *    Copyright 2009-2012 <NAME> (<EMAIL>). All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification, are
 * permitted provided that the following conditions are met:
 *
 *   1. Redistributions of source code must retain the above copyright notice, this list of
 *      conditions and the following disclaimer.
 *
 *   2. Redistributions in binary form must reproduce the above copyright notice, this list
 *      of conditions and the following disclaimer in the documentation and/or other materials
 *      provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) AND CONTRIBUTORS ``AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
 * SHALL THE COPYRIGHT HOLDER(S) OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
 * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

SLJIT_API_FUNC_ATTRIBUTE SLJIT_CONST char* sljit_get_platform_name(void)
{
	return "SPARC" SLJIT_CPUINFO;
}

/* Length of an instruction word
   Both for sparc-32 and sparc-64 */
typedef sljit_ui sljit_ins;

static void sparc_cache_flush(sljit_ins *from, sljit_ins *to)
{
#if defined(__SUNPRO_C) && __SUNPRO_C < 0x590
	__asm (
		/* if (from == to) return */
		"cmp %i0, %i1\n"
		"be .leave\n"
		"nop\n"

		/* loop until from >= to */
		".mainloop:\n"
		"flush %i0\n"
		"add %i0, 8, %i0\n"
		"cmp %i0, %i1\n"
		"bcs .mainloop\n"
		"nop\n"

		/* The comparison was done above. */
		"bne .leave\n"
		/* nop is not necessary here, since the
		   sub operation has no side effect. */
		"sub %i0, 4, %i0\n"
		"flush %i0\n"
		".leave:"
	);
#else
	if (SLJIT_UNLIKELY(from == to))
		return;

	do {
		__asm__ volatile (
			"flush %0\n"
			: : "r"(from)
		);
		/* Operates at least on doubleword. */
		from += 2;
	} while (from < to);

	if (from == to) {
		/* Flush the last word. */
		from --;
		__asm__ volatile (
			"flush %0\n"
			: : "r"(from)
		);
	}
#endif
}

/* TMP_REG2 is not used by getput_arg */
#define TMP_REG1	(SLJIT_NUMBER_OF_REGISTERS + 2)
#define TMP_REG2	(SLJIT_NUMBER_OF_REGISTERS + 3)
#define TMP_REG3	(SLJIT_NUMBER_OF_REGISTERS + 4)
#define TMP_LINK	(SLJIT_NUMBER_OF_REGISTERS + 5)

#define TMP_FREG1	(0)
#define TMP_FREG2	((SLJIT_NUMBER_OF_FLOAT_REGISTERS + 1) << 1)

static SLJIT_CONST sljit_ub reg_map[SLJIT_NUMBER_OF_REGISTERS + 6] = {
	0, 8, 9, 10, 13, 29, 28, 27, 23, 22, 21, 20, 19, 18, 17, 16, 26, 25, 24, 14, 1, 11, 12, 15
};

/* --------------------------------------------------------------------- */
/*  Instrucion forms                                                     */
/* --------------------------------------------------------------------- */

#define D(d)		(reg_map[d] << 25)
#define DA(d)		((d) << 25)
#define S1(s1)		(reg_map[s1] << 14)
#define S2(s2)		(reg_map[s2])
#define S1A(s1)		((s1) << 14)
#define S2A(s2)		(s2)
#define IMM_ARG		0x2000
#define DOP(op)		((op) << 5)
#define IMM(imm)	(((imm) & 0x1fff) | IMM_ARG)

#define DR(dr)		(reg_map[dr])
#define OPC1(opcode)	((opcode) << 30)
#define OPC2(opcode)	((opcode) << 22)
#define OPC3(opcode)	((opcode) << 19)
#define SET_FLAGS	OPC3(0x10)

#define ADD		(OPC1(0x2) | OPC3(0x00))
#define ADDC		(OPC1(0x2) | OPC3(0x08))
#define AND		(OPC1(0x2) | OPC3(0x01))
#define ANDN		(OPC1(0x2) | OPC3(0x05))
#define CALL		(OPC1(0x1))
#define FABSS		(OPC1(0x2) | OPC3(0x34) | DOP(0x09))
#define FADDD		(OPC1(0x2) | OPC3(0x34) | DOP(0x42))
#define FADDS		(OPC1(0x2) | OPC3(0x34) | DOP(0x41))
#define FCMPD		(OPC1(0x2) | OPC3(0x35) | DOP(0x52))
#define FCMPS		(OPC1(0x2) | OPC3(0x35) | DOP(0x51))
#define FDIVD		(OPC1(0x2) | OPC3(0x34) | DOP(0x4e))
#define FDIVS		(OPC1(0x2) | OPC3(0x34) | DOP(0x4d))
#define FDTOI		(OPC1(0x2) | OPC3(0x34) | DOP(0xd2))
#define FDTOS		(OPC1(0x2) | OPC3(0x34) | DOP(0xc6))
#define FITOD		(OPC1(0x2) | OPC3(0x34) | DOP(0xc8))
#define FITOS		(OPC1(0x2) | OPC3(0x34) | DOP(0xc4))
#define FMOVS		(OPC1(0x2) | OPC3(0x34) | DOP(0x01))
#define FMULD		(OPC1(0x2) | OPC3(0x34) | DOP(0x4a))
#define FMULS		(OPC1(0x2) | OPC3(0x34) | DOP(0x49))
#define FNEGS		(OPC1(0x2) | OPC3(0x34) | DOP(0x05))
#define FSTOD		(OPC1(0x2) | OPC3(0x34) | DOP(0xc9))
#define FSTOI		(OPC1(0x2) | OPC3(0x34) | DOP(0xd1))
#define FSUBD		(OPC1(0x2) | OPC3(0x34) | DOP(0x46))
#define FSUBS		(OPC1(0x2) | OPC3(0x34) | DOP(0x45))
#define JMPL		(OPC1(0x2) | OPC3(0x38))
#define NOP		(OPC1(0x0) | OPC2(0x04))
#define OR		(OPC1(0x2) | OPC3(0x02))
#define ORN		(OPC1(0x2) | OPC3(0x06))
#define RDY		(OPC1(0x2) | OPC3(0x28) | S1A(0))
#define RESTORE		(OPC1(0x2) | OPC3(0x3d))
#define SAVE		(OPC1(0x2) | OPC3(0x3c))
#define SETHI		(OPC1(0x0) | OPC2(0x04))
#define SLL		(OPC1(0x2) | OPC3(0x25))
#define SLLX		(OPC1(0x2) | OPC3(0x25) | (1 << 12))
#define SRA		(OPC1(0x2) | OPC3(0x27))
#define SRAX		(OPC1(0x2) | OPC3(0x27) | (1 << 12))
#define SRL		(OPC1(0x2) | OPC3(0x26))
#define SRLX		(OPC1(0x2) | OPC3(0x26) | (1 << 12))
#define SUB		(OPC1(0x2) | OPC3(0x04))
#define SUBC		(OPC1(0x2) | OPC3(0x0c))
#define TA		(OPC1(0x2) | OPC3(0x3a) | (8 << 25))
#define WRY		(OPC1(0x2) | OPC3(0x30) | DA(0))
#define XOR		(OPC1(0x2) | OPC3(0x03))
#define XNOR		(OPC1(0x2) | OPC3(0x07))

#if (defined SLJIT_CONFIG_SPARC_32 && SLJIT_CONFIG_SPARC_32)
#define MAX_DISP	(0x1fffff)
#define MIN_DISP	(-0x200000)
#define DISP_MASK	(0x3fffff)

#define BICC		(OPC1(0x0) | OPC2(0x2))
#define FBFCC		(OPC1(0x0) | OPC2(0x6))
#define SLL_W		SLL
#define SDIV		(OPC1(0x2) | OPC3(0x0f))
#define SMUL		(OPC1(0x2) | OPC3(0x0b))
#define UDIV		(OPC1(0x2) | OPC3(0x0e))
#define UMUL		(OPC1(0x2) | OPC3(0x0a))
#else
#define SLL_W		SLLX
#endif

#define SIMM_MAX	(0x0fff)
#define SIMM_MIN	(-0x1000)

/* dest_reg is the absolute name of the register
   Useful for reordering instructions in the delay slot. */
static sljit_si push_inst(struct sljit_compiler *compiler, sljit_ins ins, sljit_si delay_slot)
{
	sljit_ins *ptr;
	SLJIT_ASSERT((delay_slot & DST_INS_MASK) == UNMOVABLE_INS
		|| (delay_slot & DST_INS_MASK) == MOVABLE_INS
		|| (delay_slot & DST_INS_MASK) == ((ins >> 25) & 0x1f));
	ptr = (sljit_ins*)ensure_buf(compiler, sizeof(sljit_ins));
	FAIL_IF(!ptr);
	*ptr = ins;
	compiler->size++;
	compiler->delay_slot = delay_slot;
	return SLJIT_SUCCESS;
}

static SLJIT_INLINE sljit_ins* detect_jump_type(struct sljit_jump *jump, sljit_ins *code_ptr, sljit_ins *code)
{
	sljit_sw diff;
	sljit_uw target_addr;
	sljit_ins *inst;
	sljit_ins saved_inst;

	if (jump->flags & SLJIT_REWRITABLE_JUMP)
		return code_ptr;

	if (jump->flags & JUMP_ADDR)
		target_addr = jump->u.target;
	else {
		SLJIT_ASSERT(jump->flags & JUMP_LABEL);
		target_addr = (sljit_uw)(code + jump->u.label->size);
	}
	inst = (sljit_ins*)jump->addr;

#if (defined SLJIT_CONFIG_SPARC_32 && SLJIT_CONFIG_SPARC_32)
	if (jump->flags & IS_CALL) {
		/* Call is always patchable on sparc 32. */
		jump->flags |= PATCH_CALL;
		if (jump->flags & IS_MOVABLE) {
			inst[0] = inst[-1];
			inst[-1] = CALL;
			jump->addr -= sizeof(sljit_ins);
			return inst;
		}
		inst[0] = CALL;
		inst[1] = NOP;
		return inst + 1;
	}
#else
	/* Both calls and BPr instructions shall not pass this point. */
#error "Implementation required"
#endif

	if (jump->flags & IS_COND)
		inst--;

	if (jump->flags & IS_MOVABLE) {
		diff = ((sljit_sw)target_addr - (sljit_sw)(inst - 1)) >> 2;
		if (diff <= MAX_DISP && diff >= MIN_DISP) {
			jump->flags |= PATCH_B;
			inst--;
			if (jump->flags & IS_COND) {
				saved_inst = inst[0];
				inst[0] = inst[1] ^ (1 << 28);
				inst[1] = saved_inst;
			} else {
				inst[1] = inst[0];
				inst[0] = BICC | DA(0x8);
			}
			jump->addr = (sljit_uw)inst;
			return inst + 1;
		}
	}

	diff = ((sljit_sw)target_addr - (sljit_sw)(inst)) >> 2;
	if (diff <= MAX_DISP && diff >= MIN_DISP) {
		jump->flags |= PATCH_B;
		if (jump->flags & IS_COND)
			inst[0] ^= (1 << 28);
		else
			inst[0] = BICC | DA(0x8);
		inst[1] = NOP;
		jump->addr = (sljit_uw)inst;
		return inst + 1;
	}

	return code_ptr;
}

SLJIT_API_FUNC_ATTRIBUTE void* sljit_generate_code(struct sljit_compiler *compiler)
{
	struct sljit_memory_fragment *buf;
	sljit_ins *code;
	sljit_ins *code_ptr;
	sljit_ins *buf_ptr;
	sljit_ins *buf_end;
	sljit_uw word_count;
	sljit_uw addr;

	struct sljit_label *label;
	struct sljit_jump *jump;
	struct sljit_const *const_;

	CHECK_ERROR_PTR();
	CHECK_PTR(check_sljit_generate_code(compiler));
	reverse_buf(compiler);

	code = (sljit_ins*)SLJIT_MALLOC_EXEC(compiler->size * sizeof(sljit_ins));
	PTR_FAIL_WITH_EXEC_IF(code);
	buf = compiler->buf;

	code_ptr = code;
	word_count = 0;
	label = compiler->labels;
	jump = compiler->jumps;
	const_ = compiler->consts;
	do {
		buf_ptr = (sljit_ins*)buf->memory;
		buf_end = buf_ptr + (buf->used_size >> 2);
		do {
			*code_ptr = *buf_ptr++;
			SLJIT_ASSERT(!label || label->size >= word_count);
			SLJIT_ASSERT(!jump || jump->addr >= word_count);
			SLJIT_ASSERT(!const_ || const_->addr >= word_count);
			/* These structures are ordered by their address. */
			if (label && label->size == word_count) {
				/* Just recording the address. */
				label->addr = (sljit_uw)code_ptr;
				label->size = code_ptr - code;
				label = label->next;
			}
			if (jump && jump->addr == word_count) {
#if (defined SLJIT_CONFIG_SPARC_32 && SLJIT_CONFIG_SPARC_32)
				jump->addr = (sljit_uw)(code_ptr - 3);
#else
				jump->addr = (sljit_uw)(code_ptr - 6);
#endif
				code_ptr = detect_jump_type(jump, code_ptr, code);
				jump = jump->next;
			}
			if (const_ && const_->addr == word_count) {
				/* Just recording the address. */
				const_->addr = (sljit_uw)code_ptr;
				const_ = const_->next;
			}
			code_ptr ++;
			word_count ++;
		} while (buf_ptr < buf_end);

		buf = buf->next;
	} while (buf);

	if (label && label->size == word_count) {
		label->addr = (sljit_uw)code_ptr;
		label->size = code_ptr - code;
		label = label->next;
	}

	SLJIT_ASSERT(!label);
	SLJIT_ASSERT(!jump);
	SLJIT_ASSERT(!const_);
	SLJIT_ASSERT(code_ptr - code <= (sljit_si)compiler->size);

	jump = compiler->jumps;
	while (jump) {
		do {
			addr = (jump->flags & JUMP_LABEL) ? jump->u.label->addr : jump->u.target;
			buf_ptr = (sljit_ins*)jump->addr;

			if (jump->flags & PATCH_CALL) {
				addr = (sljit_sw)(addr - jump->addr) >> 2;
				SLJIT_ASSERT((sljit_sw)addr <= 0x1fffffff && (sljit_sw)addr >= -0x20000000);
				buf_ptr[0] = CALL | (addr & 0x3fffffff);
				break;
			}
			if (jump->flags & PATCH_B) {
				addr = (sljit_sw)(addr - jump->addr) >> 2;
				SLJIT_ASSERT((sljit_sw)addr <= MAX_DISP && (sljit_sw)addr >= MIN_DISP);
				buf_ptr[0] = (buf_ptr[0] & ~DISP_MASK) | (addr & DISP_MASK);
				break;
			}

			/* Set the fields of immediate loads. */
#if (defined SLJIT_CONFIG_SPARC_32 && SLJIT_CONFIG_SPARC_32)
			buf_ptr[0] = (buf_ptr[0] & 0xffc00000) | ((addr >> 10) & 0x3fffff);
			buf_ptr[1] = (buf_ptr[1] & 0xfffffc00) | (addr & 0x3ff);
#else
#error "Implementation required"
#endif
		} while (0);
		jump = jump->next;
	}


	compiler->error = SLJIT_ERR_COMPILED;
	compiler->executable_size = (code_ptr - code) * sizeof(sljit_ins);
	SLJIT_CACHE_FLUSH(code, code_ptr);
	return code;
}

/* --------------------------------------------------------------------- */
/*  Entry, exit                                                          */
/* --------------------------------------------------------------------- */

/* Creates an index in data_transfer_insts array. */
#define LOAD_DATA	0x01
#define WORD_DATA	0x00
#define BYTE_DATA	0x02
#define HALF_DATA	0x04
#define INT_DATA	0x06
#define SIGNED_DATA	0x08
/* Separates integer and floating point registers */
#define GPR_REG		0x0f
#define DOUBLE_DATA	0x10
#define SINGLE_DATA	0x12

#define MEM_MASK	0x1f

#define WRITE_BACK	0x00020
#define ARG_TEST	0x00040
#define ALT_KEEP_CACHE	0x00080
#define CUMULATIVE_OP	0x00100
#define IMM_OP		0x00200
#define SRC2_IMM	0x00400

#define REG_DEST	0x00800
#define REG2_SOURCE	0x01000
#define SLOW_SRC1	0x02000
#define SLOW_SRC2	0x04000
#define SLOW_DEST	0x08000

/* SET_FLAGS (0x10 << 19) also belong here! */

#if (defined SLJIT_CONFIG_SPARC_32 && SLJIT_CONFIG_SPARC_32)
#include "sljitNativeSPARC_32.c"
#else
#include "sljitNativeSPARC_64.c"
#endif

SLJIT_API_FUNC_ATTRIBUTE sljit_si sljit_emit_enter(struct sljit_compiler *compiler,
	sljit_si options, sljit_si args, sljit_si scratches, sljit_si saveds,
	sljit_si fscratches, sljit_si fsaveds, sljit_si local_size)
{
	CHECK_ERROR();
	CHECK(check_sljit_emit_enter(compiler, options, args, scratches, saveds, fscratches, fsaveds, local_size));
	set_emit_enter(compiler, options, args, scratches, saveds, fscratches, fsaveds, local_size);

	local_size = (local_size + SLJIT_LOCALS_OFFSET + 7) & ~0x7;
	compiler->local_size = local_size;

	if (local_size <= SIMM_MAX) {
		FAIL_IF(push_inst(compiler, SAVE | D(SLJIT_SP) | S1(SLJIT_SP) | IMM(-local_size), UNMOVABLE_INS));
	}
	else {
		FAIL_IF(load_immediate(compiler, TMP_REG1, -local_size));
		FAIL_IF(push_inst(compiler, SAVE | D(SLJIT_SP) | S1(SLJIT_SP) | S2(TMP_REG1), UNMOVABLE_INS));
	}

	/* Arguments are in their appropriate registers. */

	return SLJIT_SUCCESS;
}

SLJIT_API_FUNC_ATTRIBUTE sljit_si sljit_set_context(struct sljit_compiler *compiler,
	sljit_si options, sljit_si args, sljit_si scratches, sljit_si saveds,
	sljit_si fscratches, sljit_si fsaveds, sljit_si local_size)
{
	CHECK_ERROR();
	CHECK(check_sljit_set_context(compiler, options, args, scratches, saveds, fscratches, fsaveds, local_size));
	set_set_context(compiler, options, args, scratches, saveds, fscratches, fsaveds, local_size);

	compiler->local_size = (local_size + SLJIT_LOCALS_OFFSET + 7) & ~0x7;
	return SLJIT_SUCCESS;
}

SLJIT_API_FUNC_ATTRIBUTE sljit_si sljit_emit_return(struct sljit_compiler *compiler, sljit_si op, sljit_si src, sljit_sw srcw)
{
	CHECK_ERROR();
	CHECK(check_sljit_emit_return(compiler, op, src, srcw));

	if (op != SLJIT_MOV || !FAST_IS_REG(src)) {
		FAIL_IF(emit_mov_before_return(compiler, op, src, srcw));
		src = SLJIT_R0;
	}

	FAIL_IF(push_inst(compiler, JMPL | D(0) | S1A(31) | IMM(8), UNMOVABLE_INS));
	return push_inst(compiler, RESTORE | D(SLJIT_R0) | S1(src) | S2(0), UNMOVABLE_INS);
}

/* --------------------------------------------------------------------- */
/*  Operators                                                            */
/* --------------------------------------------------------------------- */

#if (defined SLJIT_CONFIG_SPARC_32 && SLJIT_CONFIG_SPARC_32)
#define ARCH_32_64(a, b)	a
#else
#define ARCH_32_64(a, b)	b
#endif

static SLJIT_CONST sljit_ins data_transfer_insts[16 + 4] = {
/* u w s */ ARCH_32_64(OPC1(3) | OPC3(0x04) /* stw */, OPC1(3) | OPC3(0x0e) /* stx */),
/* u w l */ ARCH_32_64(OPC1(3) | OPC3(0x00) /* lduw */, OPC1(3) | OPC3(0x0b) /* ldx */),
/* u b s */ OPC1(3) | OPC3(0x05) /* stb */,
/* u b l */ OPC1(3) | OPC3(0x01) /* ldub */,
/* u h s */ OPC1(3) | OPC3(0x06) /* sth */,
/* u h l */ OPC1(3) | OPC3(0x02) /* lduh */,
/* u i s */ OPC1(3) | OPC3(0x04) /* stw */,
/* u i l */ OPC1(3) | OPC3(0x00) /* lduw */,

/* s w s */ ARCH_32_64(OPC1(3) | OPC3(0x04) /* stw */, OPC1(3) | OPC3(0x0e) /* stx */),
/* s w l */ ARCH_32_64(OPC1(3) | OPC3(0x00) /* lduw */, OPC1(3) | OPC3(0x0b) /* ldx */),
/* s b s */ OPC1(3) | OPC3(0x05) /* stb */,
/* s b l */ OPC1(3) | OPC3(0x09) /* ldsb */,
/* s h s */ OPC1(3) | OPC3(0x06) /* sth */,
/* s h l */ OPC1(3) | OPC3(0x0a) /* ldsh */,
/* s i s */ OPC1(3) | OPC3(0x04) /* stw */,
/* s i l */ ARCH_32_64(OPC1(3) | OPC3(0x00) /* lduw */, OPC1(3) | OPC3(0x08) /* ldsw */),

/* d   s */ OPC1(3) | OPC3(0x27),
/* d   l */ OPC1(3) | OPC3(0x23),
/* s   s */ OPC1(3) | OPC3(0x24),
/* s   l */ OPC1(3) | OPC3(0x20),
};

#undef ARCH_32_64

/* Can perform an operation using at most 1 instruction. */
static sljit_si getput_arg_fast(struct sljit_compiler *compiler, sljit_si flags, sljit_si reg, sljit_si arg, sljit_sw argw)
{
	SLJIT_ASSERT(arg & SLJIT_MEM);

	if (!(flags & WRITE_BACK) || !(arg & REG_MASK)) {
		if ((!(arg & OFFS_REG_MASK) && argw <= SIMM_MAX && argw >= SIMM_MIN)
				|| ((arg & OFFS_REG_MASK) && (argw & 0x3) == 0)) {
			/* Works for both absoulte and relative addresses (immediate case). */
			if (SLJIT_UNLIKELY(flags & ARG_TEST))
				return 1;
			FAIL_IF(push_inst(compiler, data_transfer_insts[flags & MEM_MASK]
				| ((flags & MEM_MASK) <= GPR_REG ? D(reg) : DA(reg))
				| S1(arg & REG_MASK) | ((arg & OFFS_REG_MASK) ? S2(OFFS_REG(arg)) : IMM(argw)),
				((flags & MEM_MASK) <= GPR_REG && (flags & LOAD_DATA)) ? DR(reg) : MOVABLE_INS));
			return -1;
		}
	}
	return 0;
}

/* See getput_arg below.
   Note: can_cache is called only for binary operators. Those
   operators always uses word arguments without write back. */
static sljit_si can_cache(sljit_si arg, sljit_sw argw, sljit_si next_arg, sljit_sw next_argw)
{
	SLJIT_ASSERT((arg & SLJIT_MEM) && (next_arg & SLJIT_MEM));

	/* Simple operation except for updates. */
	if (arg & OFFS_REG_MASK) {
		argw &= 0x3;
		SLJIT_ASSERT(argw);
		next_argw &= 0x3;
		if ((arg & OFFS_REG_MASK) == (next_arg & OFFS_REG_MASK) && argw == next_argw)
			return 1;
		return 0;
	}

	if (((next_argw - argw) <= SIMM_MAX && (next_argw - argw) >= SIMM_MIN))
		return 1;
	return 0;
}

/* Emit the necessary instructions. See can_cache above. */
static sljit_si getput_arg(struct sljit_compiler *compiler, sljit_si flags, sljit_si reg, sljit_si arg, sljit_sw argw, sljit_si next_arg, sljit_sw next_argw)
{
	sljit_si base, arg2, delay_slot;
	sljit_ins dest;

	SLJIT_ASSERT(arg & SLJIT_MEM);
	if (!(next_arg & SLJIT_MEM)) {
		next_arg = 0;
		next_argw = 0;
	}

	base = arg & REG_MASK;
	if (SLJIT_UNLIKELY(arg & OFFS_REG_MASK)) {
		argw &= 0x3;
		SLJIT_ASSERT(argw != 0);

		/* Using the cache. */
		if (((SLJIT_MEM | (arg & OFFS_REG_MASK)) == compiler->cache_arg) && (argw == compiler->cache_argw))
			arg2 = TMP_REG3;
		else {
			if ((arg & OFFS_REG_MASK) == (next_arg & OFFS_REG_MASK) && argw == (next_argw & 0x3)) {
				compiler->cache_arg = SLJIT_MEM | (arg & OFFS_REG_MASK);
				compiler->cache_argw = argw;
				arg2 = TMP_REG3;
			}
			else if ((flags & LOAD_DATA) && ((flags & MEM_MASK) <= GPR_REG) && reg != base && reg != OFFS_REG(arg))
				arg2 = reg;
			else /* It must be a mov operation, so tmp1 must be free to use. */
				arg2 = TMP_REG1;
			FAIL_IF(push_inst(compiler, SLL_W | D(arg2) | S1(OFFS_REG(arg)) | IMM_ARG | argw, DR(arg2)));
		}
	}
	else {
		/* Using the cache. */
		if ((compiler->cache_arg == SLJIT_MEM) && (argw - compiler->cache_argw) <= SIMM_MAX && (argw - compiler->cache_argw) >= SIMM_MIN) {
			if (argw != compiler->cache_argw) {
				FAIL_IF(push_inst(compiler, ADD | D(TMP_REG3) | S1(TMP_REG3) | IMM(argw - compiler->cache_argw), DR(TMP_REG3)));
				compiler->cache_argw = argw;
			}
			arg2 = TMP_REG3;
		} else {
			if ((next_argw - argw) <= SIMM_MAX && (next_argw - argw) >= SIMM_MIN) {
				compiler->cache_arg = SLJIT_MEM;
				compiler->cache_argw = argw;
				arg2 = TMP_REG3;
			}
			else if ((flags & LOAD_DATA) && ((flags & MEM_MASK) <= GPR_REG) && reg != base)
				arg2 = reg;
			else /* It must be a mov operation, so tmp1 must be free to use. */
				arg2 = TMP_REG1;
			FAIL_IF(load_immediate(compiler, arg2, argw));
		}
	}

	dest = ((flags & MEM_MASK) <= GPR_REG ? D(reg) : DA(reg));
	delay_slot = ((flags & MEM_MASK) <= GPR_REG && (flags & LOAD_DATA)) ? DR(reg) : MOVABLE_INS;
	if (!base)
		return push_inst(compiler, data_transfer_insts[flags & MEM_MASK] | dest | S1(arg2) | IMM(0), delay_slot);
	if (!(flags & WRITE_BACK))
		return push_inst(compiler, data_transfer_insts[flags & MEM_MASK] | dest | S1(base) | S2(arg2), delay_slot);
	FAIL_IF(push_inst(compiler, data_transfer_insts[flags & MEM_MASK] | dest | S1(base) | S2(arg2), delay_slot));
	return push_inst(compiler, ADD | D(base) | S1(base) | S2(arg2), DR(base));
}

static SLJIT_INLINE sljit_si emit_op_mem(struct sljit_compiler *compiler, sljit_si flags, sljit_si reg, sljit_si arg, sljit_sw argw)
{
	if (getput_arg_fast(compiler, flags, reg, arg, argw))
		return compiler->error;
	compiler->cache_arg = 0;
	compiler->cache_argw = 0;
	return getput_arg(compiler, flags, reg, arg, argw, 0, 0);
}

static SLJIT_INLINE sljit_si emit_op_mem2(struct sljit_compiler *compiler, sljit_si flags, sljit_si reg, sljit_si arg1, sljit_sw arg1w, sljit_si arg2, sljit_sw arg2w)
{
	if (getput_arg_fast(compiler, flags, reg, arg1, arg1w))
		return compiler->error;
	return getput_arg(compiler, flags, reg, arg1, arg1w, arg2, arg2w);
}

static sljit_si emit_op(struct sljit_compiler *compiler, sljit_si op, sljit_si flags,
	sljit_si dst, sljit_sw dstw,
	sljit_si src1, sljit_sw src1w,
	sljit_si src2, sljit_sw src2w)
{
	/* arg1 goes to TMP_REG1 or src reg
	   arg2 goes to TMP_REG2, imm or src reg
	   TMP_REG3 can be used for caching
	   result goes to TMP_REG2, so put result can use TMP_REG1 and TMP_REG3. */
	sljit_si dst_r = TMP_REG2;
	sljit_si src1_r;
	sljit_sw src2_r = 0;
	sljit_si sugg_src2_r = TMP_REG2;

	if (!(flags & ALT_KEEP_CACHE)) {
		compiler->cache_arg = 0;
		compiler->cache_argw = 0;
	}

	if (SLJIT_UNLIKELY(dst == SLJIT_UNUSED)) {
		if (op >= SLJIT_MOV && op <= SLJIT_MOVU_SI && !(src2 & SLJIT_MEM))
			return SLJIT_SUCCESS;
	}
	else if (FAST_IS_REG(dst)) {
		dst_r = dst;
		flags |= REG_DEST;
		if (op >= SLJIT_MOV && op <= SLJIT_MOVU_SI)
			sugg_src2_r = dst_r;
	}
	else if ((dst & SLJIT_MEM) && !getput_arg_fast(compiler, flags | ARG_TEST, TMP_REG1, dst, dstw))
		flags |= SLOW_DEST;

	if (flags & IMM_OP) {
		if ((src2 & SLJIT_IMM) && src2w) {
			if (src2w <= SIMM_MAX && src2w >= SIMM_MIN) {
				flags |= SRC2_IMM;
				src2_r = src2w;
			}
		}
		if (!(flags & SRC2_IMM) && (flags & CUMULATIVE_OP) && (src1 & SLJIT_IMM) && src1w) {
			if (src1w <= SIMM_MAX && src1w >= SIMM_MIN) {
				flags |= SRC2_IMM;
				src2_r = src1w;

				/* And swap arguments. */
				src1 = src2;
				src1w = src2w;
				src2 = SLJIT_IMM;
				/* src2w = src2_r unneeded. */
			}
		}
	}

	/* Source 1. */
	if (FAST_IS_REG(src1))
		src1_r = src1;
	else if (src1 & SLJIT_IMM) {
		if (src1w) {
			FAIL_IF(load_immediate(compiler, TMP_REG1, src1w));
			src1_r = TMP_REG1;
		}
		else
			src1_r = 0;
	}
	else {
		if (getput_arg_fast(compiler, flags | LOAD_DATA, TMP_REG1, src1, src1w))
			FAIL_IF(compiler->error);
		else
			flags |= SLOW_SRC1;
		src1_r = TMP_REG1;
	}

	/* Source 2. */
	if (FAST_IS_REG(src2)) {
		src2_r = src2;
		flags |= REG2_SOURCE;
		if (!(flags & REG_DEST) && op >= SLJIT_MOV && op <= SLJIT_MOVU_SI)
			dst_r = src2_r;
	}
	else if (src2 & SLJIT_IMM) {
		if (!(flags & SRC2_IMM)) {
			if (src2w) {
				FAIL_IF(load_immediate(compiler, sugg_src2_r, src2w));
				src2_r = sugg_src2_r;
			}
			else {
				src2_r = 0;
				if ((op >= SLJIT_MOV && op <= SLJIT_MOVU_SI) && (dst & SLJIT_MEM))
					dst_r = 0;
			}
		}
	}
	else {
		if (getput_arg_fast(compiler, flags | LOAD_DATA, sugg_src2_r, src2, src2w))
			FAIL_IF(compiler->error);
		else
			flags |= SLOW_SRC2;
		src2_r = sugg_src2_r;
	}

	if ((flags & (SLOW_SRC1 | SLOW_SRC2)) == (SLOW_SRC1 | SLOW_SRC2)) {
		SLJIT_ASSERT(src2_r == TMP_REG2);
		if (!can_cache(src1, src1w, src2, src2w) && can_cache(src1, src1w, dst, dstw)) {
			FAIL_IF(getput_arg(compiler, flags | LOAD_DATA, TMP_REG2, src2, src2w, src1, src1w));
			FAIL_IF(getput_arg(compiler, flags | LOAD_DATA, TMP_REG1, src1, src1w, dst, dstw));
		}
		else {
			FAIL_IF(getput_arg(compiler, flags | LOAD_DATA, TMP_REG1, src1, src1w, src2, src2w));
			FAIL_IF(getput_arg(compiler, flags | LOAD_DATA, TMP_REG2, src2, src2w, dst, dstw));
		}
	}
	else if (flags & SLOW_SRC1)
		FAIL_IF(getput_arg(compiler, flags | LOAD_DATA, TMP_REG1, src1, src1w, dst, dstw));
	else if (flags & SLOW_SRC2)
		FAIL_IF(getput_arg(compiler, flags | LOAD_DATA, sugg_src2_r, src2, src2w, dst, dstw));

	FAIL_IF(emit_single_op(compiler, op, flags, dst_r, src1_r, src2_r));

	if (dst & SLJIT_MEM) {
		if (!(flags & SLOW_DEST)) {
			getput_arg_fast(compiler, flags, dst_r, dst, dstw);
			return compiler->error;
		}
		return getput_arg(compiler, flags, dst_r, dst, dstw, 0, 0);
	}

	return SLJIT_SUCCESS;
}

SLJIT_API_FUNC_ATTRIBUTE sljit_si sljit_emit_op0(struct sljit_compiler *compiler, sljit_si op)
{
	CHECK_ERROR();
	CHECK(check_sljit_emit_op0(compiler, op));

	op = GET_OPCODE(op);
	switch (op) {
	case SLJIT_BREAKPOINT:
		return push_inst(compiler, TA, UNMOVABLE_INS);
	case SLJIT_NOP:
		return push_inst(compiler, NOP, UNMOVABLE_INS);
	case SLJIT_LUMUL:
	case SLJIT_LSMUL:
#if (defined SLJIT_CONFIG_SPARC_32 && SLJIT_CONFIG_SPARC_32)
		FAIL_IF(push_inst(compiler, (op == SLJIT_LUMUL ? UMUL : SMUL) | D(SLJIT_R0) | S1(SLJIT_R0) | S2(SLJIT_R1), DR(SLJIT_R0)));
		return push_inst(compiler, RDY | D(SLJIT_R1), DR(SLJIT_R1));
#else
#error "Implementation required"
#endif
	case SLJIT_UDIVMOD:
	case SLJIT_SDIVMOD:
	case SLJIT_UDIVI:
	case SLJIT_SDIVI:
		SLJIT_COMPILE_ASSERT((SLJIT_UDIVMOD & 0x2) == 0 && SLJIT_UDIVI - 0x2 == SLJIT_UDIVMOD, bad_div_opcode_assignments);
#if (defined SLJIT_CONFIG_SPARC_32 && SLJIT_CONFIG_SPARC_32)
		if ((op | 0x2) == SLJIT_UDIVI)
			FAIL_IF(push_inst(compiler, WRY | S1(0), MOVABLE_INS));
		else {
			FAIL_IF(push_inst(compiler, SRA | D(TMP_REG1) | S1(SLJIT_R0) | IMM(31), DR(TMP_REG1)));
			FAIL_IF(push_inst(compiler, WRY | S1(TMP_REG1), MOVABLE_INS));
		}
		if (op <= SLJIT_SDIVMOD)
			FAIL_IF(push_inst(compiler, OR | D(TMP_REG2) | S1(0) | S2(SLJIT_R0), DR(TMP_REG2)));
		FAIL_IF(push_inst(compiler, ((op | 0x2) == SLJIT_UDIVI ? UDIV : SDIV) | D(SLJIT_R0) | S1(SLJIT_R0) | S2(SLJIT_R1), DR(SLJIT_R0)));
		if (op >= SLJIT_UDIVI)
			return SLJIT_SUCCESS;
		FAIL_IF(push_inst(compiler, SMUL | D(SLJIT_R1) | S1(SLJIT_R0) | S2(SLJIT_R1), DR(SLJIT_R1)));
		return push_inst(compiler, SUB | D(SLJIT_R1) | S1(TMP_REG2) | S2(SLJIT_R1), DR(SLJIT_R1));
#else
#error "Implementation required"
#endif
	}

	return SLJIT_SUCCESS;
}

SLJIT_API_FUNC_ATTRIBUTE sljit_si sljit_emit_op1(struct sljit_compiler *compiler, sljit_si op,
	sljit_si dst, sljit_sw dstw,
	sljit_si src, sljit_sw srcw)
{
	sljit_si flags = GET_FLAGS(op) ? SET_FLAGS : 0;

	CHECK_ERROR();
	CHECK(check_sljit_emit_op1(compiler, op, dst, dstw, src, srcw));
	ADJUST_LOCAL_OFFSET(dst, dstw);
	ADJUST_LOCAL_OFFSET(src, srcw);

	op = GET_OPCODE(op);
	switch (op) {
	case SLJIT_MOV:
	case SLJIT_MOV_P:
		return emit_op(compiler, SLJIT_MOV, flags | WORD_DATA, dst, dstw, TMP_REG1, 0, src, srcw);

	case SLJIT_MOV_UI:
		return emit_op(compiler, SLJIT_MOV_UI, flags | INT_DATA, dst, dstw, TMP_REG1, 0, src, srcw);

	case SLJIT_MOV_SI:
		return emit_op(compiler, SLJIT_MOV_SI, flags | INT_DATA | SIGNED_DATA, dst, dstw, TMP_REG1, 0, src, srcw);

	case SLJIT_MOV_UB:
		return emit_op(compiler, SLJIT_MOV_UB, flags | BYTE_DATA, dst, dstw, TMP_REG1, 0, src, (src & SLJIT_IMM) ? (sljit_ub)srcw : srcw);

	case SLJIT_MOV_SB:
		return emit_op(compiler, SLJIT_MOV_SB, flags | BYTE_DATA | SIGNED_DATA, dst, dstw, TMP_REG1, 0, src, (src & SLJIT_IMM) ? (sljit_sb)srcw : srcw);

	case SLJIT_MOV_UH:
		return emit_op(compiler, SLJIT_MOV_UH, flags | HALF_DATA, dst, dstw, TMP_REG1, 0, src, (src & SLJIT_IMM) ? (sljit_uh)srcw : srcw);

	case SLJIT_MOV_SH:
		return emit_op(compiler, SLJIT_MOV_SH, flags | HALF_DATA | SIGNED_DATA, dst, dstw, TMP_REG1, 0, src, (src & SLJIT_IMM) ? (sljit_sh)srcw : srcw);

	case SLJIT_MOVU:
	case SLJIT_MOVU_P:
		return emit_op(compiler, SLJIT_MOV, flags | WORD_DATA | WRITE_BACK, dst, dstw, TMP_REG1, 0, src, srcw);

	case SLJIT_MOVU_UI:
		return emit_op(compiler, SLJIT_MOV_UI, flags | INT_DATA | WRITE_BACK, dst, dstw, TMP_REG1, 0, src, srcw);

	case SLJIT_MOVU_SI:
		return emit_op(compiler, SLJIT_MOV_SI, flags | INT_DATA | SIGNED_DATA | WRITE_BACK, dst, dstw, TMP_REG1, 0, src, srcw);

	case SLJIT_MOVU_UB:
		return emit_op(compiler, SLJIT_MOV_UB, flags | BYTE_DATA | WRITE_BACK, dst, dstw, TMP_REG1, 0, src, (src & SLJIT_IMM) ? (sljit_ub)srcw : srcw);

	case SLJIT_MOVU_SB:
		return emit_op(compiler, SLJIT_MOV_SB, flags | BYTE_DATA | SIGNED_DATA | WRITE_BACK, dst, dstw, TMP_REG1, 0, src, (src & SLJIT_IMM) ? (sljit_sb)srcw : srcw);

	case SLJIT_MOVU_UH:
		return emit_op(compiler, SLJIT_MOV_UH, flags | HALF_DATA | WRITE_BACK, dst, dstw, TMP_REG1, 0, src, (src & SLJIT_IMM) ? (sljit_uh)srcw : srcw);

	case SLJIT_MOVU_SH:
		return emit_op(compiler, SLJIT_MOV_SH, flags | HALF_DATA | SIGNED_DATA | WRITE_BACK, dst, dstw, TMP_REG1, 0, src, (src & SLJIT_IMM) ? (sljit_sh)srcw : srcw);

	case SLJIT_NOT:
	case SLJIT_CLZ:
		return emit_op(compiler, op, flags, dst, dstw, TMP_REG1, 0, src, srcw);

	case SLJIT_NEG:
		return emit_op(compiler, SLJIT_SUB, flags | IMM_OP, dst, dstw, SLJIT_IMM, 0, src, srcw);
	}

	return SLJIT_SUCCESS;
}

SLJIT_API_FUNC_ATTRIBUTE sljit_si sljit_emit_op2(struct sljit_compiler *compiler, sljit_si op,
	sljit_si dst, sljit_sw dstw,
	sljit_si src1, sljit_sw src1w,
	sljit_si src2, sljit_sw src2w)
{
	sljit_si flags = GET_FLAGS(op) ? SET_FLAGS : 0;

	CHECK_ERROR();
	CHECK(check_sljit_emit_op2(compiler, op, dst, dstw, src1, src1w, src2, src2w));
	ADJUST_LOCAL_OFFSET(dst, dstw);
	ADJUST_LOCAL_OFFSET(src1, src1w);
	ADJUST_LOCAL_OFFSET(src2, src2w);

	op = GET_OPCODE(op);
	switch (op) {
	case SLJIT_ADD:
	case SLJIT_ADDC:
	case SLJIT_MUL:
	case SLJIT_AND:
	case SLJIT_OR:
	case SLJIT_XOR:
		return emit_op(compiler, op, flags | CUMULATIVE_OP | IMM_OP, dst, dstw, src1, src1w, src2, src2w);

	case SLJIT_SUB:
	case SLJIT_SUBC:
		return emit_op(compiler, op, flags | IMM_OP, dst, dstw, src1, src1w, src2, src2w);

	case SLJIT_SHL:
	case SLJIT_LSHR:
	case SLJIT_ASHR:
#if (defined SLJIT_CONFIG_SPARC_32 && SLJIT_CONFIG_SPARC_32)
		if (src2 & SLJIT_IMM)
			src2w &= 0x1f;
#else
		SLJIT_ASSERT_STOP();
#endif
		return emit_op(compiler, op, flags | IMM_OP, dst, dstw, src1, src1w, src2, src2w);
	}

	return SLJIT_SUCCESS;
}

SLJIT_API_FUNC_ATTRIBUTE sljit_si sljit_get_register_index(sljit_si reg)
{
	CHECK_REG_INDEX(check_sljit_get_register_index(reg));
	return reg_map[reg];
}

SLJIT_API_FUNC_ATTRIBUTE sljit_si sljit_get_float_register_index(sljit_si reg)
{
	CHECK_REG_INDEX(check_sljit_get_float_register_index(reg));
	return reg << 1;
}

SLJIT_API_FUNC_ATTRIBUTE sljit_si sljit_emit_op_custom(struct sljit_compiler *compiler,
	void *instruction, sljit_si size)
{
	CHECK_ERROR();
	CHECK(check_sljit_emit_op_custom(compiler, instruction, size));

	return push_inst(compiler, *(sljit_ins*)instruction, UNMOVABLE_INS);
}

/* --------------------------------------------------------------------- */
/*  Floating point operators                                             */
/* --------------------------------------------------------------------- */

SLJIT_API_FUNC_ATTRIBUTE sljit_si sljit_is_fpu_available(void)
{
#ifdef SLJIT_IS_FPU_AVAILABLE
	return SLJIT_IS_FPU_AVAILABLE;
#else
	/* Available by default. */
	return 1;
#endif
}

#define FLOAT_DATA(op) (DOUBLE_DATA | ((op & SLJIT_SINGLE_OP) >> 7))
#define SELECT_FOP(op, single, double) ((op & SLJIT_SINGLE_OP) ? single : double)
#define FLOAT_TMP_MEM_OFFSET (22 * sizeof(sljit_sw))

static SLJIT_INLINE sljit_si sljit_emit_fop1_convw_fromd(struct sljit_compiler *compiler, sljit_si op,
	sljit_si dst, sljit_sw dstw,
	sljit_si src, sljit_sw srcw)
{
	if (src & SLJIT_MEM) {
		FAIL_IF(emit_op_mem2(compiler, FLOAT_DATA(op) | LOAD_DATA, TMP_FREG1, src, srcw, dst, dstw));
		src = TMP_FREG1;
	}
	else
		src <<= 1;

	FAIL_IF(push_inst(compiler, SELECT_FOP(op, FSTOI, FDTOI) | DA(TMP_FREG1) | S2A(src), MOVABLE_INS));

	if (dst == SLJIT_UNUSED)
		return SLJIT_SUCCESS;

	if (FAST_IS_REG(dst)) {
		FAIL_IF(emit_op_mem2(compiler, SINGLE_DATA, TMP_FREG1, SLJIT_MEM1(SLJIT_SP), FLOAT_TMP_MEM_OFFSET, SLJIT_MEM1(SLJIT_SP), FLOAT_TMP_MEM_OFFSET));
		return emit_op_mem2(compiler, WORD_DATA | LOAD_DATA, dst, SLJIT_MEM1(SLJIT_SP), FLOAT_TMP_MEM_OFFSET, SLJIT_MEM1(SLJIT_SP), FLOAT_TMP_MEM_OFFSET);
	}

	/* Store the integer value from a VFP register. */
	return emit_op_mem2(compiler, SINGLE_DATA, TMP_FREG1, dst, dstw, 0, 0);
}

static SLJIT_INLINE sljit_si sljit_emit_fop1_convd_fromw(struct sljit_compiler *compiler, sljit_si op,
	sljit_si dst, sljit_sw dstw,
	sljit_si src, sljit_sw srcw)
{
	sljit_si dst_r = FAST_IS_REG(dst) ? (dst << 1) : TMP_FREG1;

	if (src & SLJIT_IMM) {
#if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64)
		if (GET_OPCODE(op) == SLJIT_CONVD_FROMI)
			srcw = (sljit_si)srcw;
#endif
		FAIL_IF(load_immediate(compiler, TMP_REG1, srcw));
		src = TMP_REG1;
		srcw = 0;
	}

	if (FAST_IS_REG(src)) {
		FAIL_IF(emit_op_mem2(compiler, WORD_DATA, src, SLJIT_MEM1(SLJIT_SP), FLOAT_TMP_MEM_OFFSET, SLJIT_MEM1(SLJIT_SP), FLOAT_TMP_MEM_OFFSET));
		src = SLJIT_MEM1(SLJIT_SP);
		srcw = FLOAT_TMP_MEM_OFFSET;
	}

	FAIL_IF(emit_op_mem2(compiler, SINGLE_DATA | LOAD_DATA, TMP_FREG1, src, srcw, dst, dstw));
	FAIL_IF(push_inst(compiler, SELECT_FOP(op, FITOS, FITOD) | DA(dst_r) | S2A(TMP_FREG1), MOVABLE_INS));

	if (dst & SLJIT_MEM)
		return emit_op_mem2(compiler, FLOAT_DATA(op), TMP_FREG1, dst, dstw, 0, 0);
	return SLJIT_SUCCESS;
}

static SLJIT_INLINE sljit_si sljit_emit_fop1_cmp(struct sljit_compiler *compiler, sljit_si op,
	sljit_si src1, sljit_sw src1w,
	sljit_si src2, sljit_sw src2w)
{
	if (src1 & SLJIT_MEM) {
		FAIL_IF(emit_op_mem2(compiler, FLOAT_DATA(op) | LOAD_DATA, TMP_FREG1, src1, src1w, src2, src2w));
		src1 = TMP_FREG1;
	}
	else
		src1 <<= 1;

	if (src2 & SLJIT_MEM) {
		FAIL_IF(emit_op_mem2(compiler, FLOAT_DATA(op) | LOAD_DATA, TMP_FREG2, src2, src2w, 0, 0));
		src2 = TMP_FREG2;
	}
	else
		src2 <<= 1;

	return push_inst(compiler, SELECT_FOP(op, FCMPS, FCMPD) | S1A(src1) | S2A(src2), FCC_IS_SET | MOVABLE_INS);
}

SLJIT_API_FUNC_ATTRIBUTE sljit_si sljit_emit_fop1(struct sljit_compiler *compiler, sljit_si op,
	sljit_si dst, sljit_sw dstw,
	sljit_si src, sljit_sw srcw)
{
	sljit_si dst_r;

	CHECK_ERROR();
	compiler->cache_arg = 0;
	compiler->cache_argw = 0;

	SLJIT_COMPILE_ASSERT((SLJIT_SINGLE_OP == 0x100) && !(DOUBLE_DATA & 0x2), float_transfer_bit_error);
	SELECT_FOP1_OPERATION_WITH_CHECKS(compiler, op, dst, dstw, src, srcw);

	if (GET_OPCODE(op) == SLJIT_CONVD_FROMS)
		op ^= SLJIT_SINGLE_OP;

	dst_r = FAST_IS_REG(dst) ? (dst << 1) : TMP_FREG1;

	if (src & SLJIT_MEM) {
		FAIL_IF(emit_op_mem2(compiler, FLOAT_DATA(op) | LOAD_DATA, dst_r, src, srcw, dst, dstw));
		src = dst_r;
	}
	else
		src <<= 1;

	switch (GET_OPCODE(op)) {
	case SLJIT_DMOV:
		if (src != dst_r) {
			if (dst_r != TMP_FREG1) {
				FAIL_IF(push_inst(compiler, FMOVS | DA(dst_r) | S2A(src), MOVABLE_INS));
				if (!(op & SLJIT_SINGLE_OP))
					FAIL_IF(push_inst(compiler, FMOVS | DA(dst_r | 1) | S2A(src | 1), MOVABLE_INS));
			}
			else
				dst_r = src;
		}
		break;
	case SLJIT_DNEG:
		FAIL_IF(push_inst(compiler, FNEGS | DA(dst_r) | S2A(src), MOVABLE_INS));
		if (dst_r != src && !(op & SLJIT_SINGLE_OP))
			FAIL_IF(push_inst(compiler, FMOVS | DA(dst_r | 1) | S2A(src | 1), MOVABLE_INS));
		break;
	case SLJIT_DABS:
		FAIL_IF(push_inst(compiler, FABSS | DA(dst_r) | S2A(src), MOVABLE_INS));
		if (dst_r != src && !(op & SLJIT_SINGLE_OP))
			FAIL_IF(push_inst(compiler, FMOVS | DA(dst_r | 1) | S2A(src | 1), MOVABLE_INS));
		break;
	case SLJIT_CONVD_FROMS:
		FAIL_IF(push_inst(compiler, SELECT_FOP(op, FSTOD, FDTOS) | DA(dst_r) | S2A(src), MOVABLE_INS));
		op ^= SLJIT_SINGLE_OP;
		break;
	}

	if (dst & SLJIT_MEM)
		FAIL_IF(emit_op_mem2(compiler, FLOAT_DATA(op), dst_r, dst, dstw, 0, 0));
	return SLJIT_SUCCESS;
}

SLJIT_API_FUNC_ATTRIBUTE sljit_si sljit_emit_fop2(struct sljit_compiler *compiler, sljit_si op,
	sljit_si dst, sljit_sw dstw,
	sljit_si src1, sljit_sw src1w,
	sljit_si src2, sljit_sw src2w)
{
	sljit_si dst_r, flags = 0;

	CHECK_ERROR();
	CHECK(check_sljit_emit_fop2(compiler, op, dst, dstw, src1, src1w, src2, src2w));
	ADJUST_LOCAL_OFFSET(dst, dstw);
	ADJUST_LOCAL_OFFSET(src1, src1w);
	ADJUST_LOCAL_OFFSET(src2, src2w);

	compiler->cache_arg = 0;
	compiler->cache_argw = 0;

	dst_r = FAST_IS_REG(dst) ? (dst << 1) : TMP_FREG2;

	if (src1 & SLJIT_MEM) {
		if (getput_arg_fast(compiler, FLOAT_DATA(op) | LOAD_DATA, TMP_FREG1, src1, src1w)) {
			FAIL_IF(compiler->error);
			src1 = TMP_FREG1;
		} else
			flags |= SLOW_SRC1;
	}
	else
		src1 <<= 1;

	if (src2 & SLJIT_MEM) {
		if (getput_arg_fast(compiler, FLOAT_DATA(op) | LOAD_DATA, TMP_FREG2, src2, src2w)) {
			FAIL_IF(compiler->error);
			src2 = TMP_FREG2;
		} else
			flags |= SLOW_SRC2;
	}
	else
		src2 <<= 1;

	if ((flags & (SLOW_SRC1 | SLOW_SRC2)) == (SLOW_SRC1 | SLOW_SRC2)) {
		if (!can_cache(src1, src1w, src2, src2w) && can_cache(src1, src1w, dst, dstw)) {
			FAIL_IF(getput_arg(compiler, FLOAT_DATA(op) | LOAD_DATA, TMP_FREG2, src2, src2w, src1, src1w));
			FAIL_IF(getput_arg(compiler, FLOAT_DATA(op) | LOAD_DATA, TMP_FREG1, src1, src1w, dst, dstw));
		}
		else {
			FAIL_IF(getput_arg(compiler, FLOAT_DATA(op) | LOAD_DATA, TMP_FREG1, src1, src1w, src2, src2w));
			FAIL_IF(getput_arg(compiler, FLOAT_DATA(op) | LOAD_DATA, TMP_FREG2, src2, src2w, dst, dstw));
		}
	}
	else if (flags & SLOW_SRC1)
		FAIL_IF(getput_arg(compiler, FLOAT_DATA(op) | LOAD_DATA, TMP_FREG1, src1, src1w, dst, dstw));
	else if (flags & SLOW_SRC2)
		FAIL_IF(getput_arg(compiler, FLOAT_DATA(op) | LOAD_DATA, TMP_FREG2, src2, src2w, dst, dstw));

	if (flags & SLOW_SRC1)
		src1 = TMP_FREG1;
	if (flags & SLOW_SRC2)
		src2 = TMP_FREG2;

	switch (GET_OPCODE(op)) {
	case SLJIT_DADD:
		FAIL_IF(push_inst(compiler, SELECT_FOP(op, FADDS, FADDD) | DA(dst_r) | S1A(src1) | S2A(src2), MOVABLE_INS));
		break;

	case SLJIT_DSUB:
		FAIL_IF(push_inst(compiler, SELECT_FOP(op, FSUBS, FSUBD) | DA(dst_r) | S1A(src1) | S2A(src2), MOVABLE_INS));
		break;

	case SLJIT_DMUL:
		FAIL_IF(push_inst(compiler, SELECT_FOP(op, FMULS, FMULD) | DA(dst_r) | S1A(src1) | S2A(src2), MOVABLE_INS));
		break;

	case SLJIT_DDIV:
		FAIL_IF(push_inst(compiler, SELECT_FOP(op, FDIVS, FDIVD) | DA(dst_r) | S1A(src1) | S2A(src2), MOVABLE_INS));
		break;
	}

	if (dst_r == TMP_FREG2)
		FAIL_IF(emit_op_mem2(compiler, FLOAT_DATA(op), TMP_FREG2, dst, dstw, 0, 0));

	return SLJIT_SUCCESS;
}

#undef FLOAT_DATA
#undef SELECT_FOP

/* --------------------------------------------------------------------- */
/*  Other instructions                                                   */
/* --------------------------------------------------------------------- */

SLJIT_API_FUNC_ATTRIBUTE sljit_si sljit_emit_fast_enter(struct sljit_compiler *compiler, sljit_si dst, sljit_sw dstw)
{
	CHECK_ERROR();
	CHECK(check_sljit_emit_fast_enter(compiler, dst, dstw));
	ADJUST_LOCAL_OFFSET(dst, dstw);

	/* For UNUSED dst. Uncommon, but possible. */
	if (dst == SLJIT_UNUSED)
		return SLJIT_SUCCESS;

	if (FAST_IS_REG(dst))
		return push_inst(compiler, OR | D(dst) | S1(0) | S2(TMP_LINK), DR(dst));

	/* Memory. */
	return emit_op_mem(compiler, WORD_DATA, TMP_LINK, dst, dstw);
}

SLJIT_API_FUNC_ATTRIBUTE sljit_si sljit_emit_fast_return(struct sljit_compiler *compiler, sljit_si src, sljit_sw srcw)
{
	CHECK_ERROR();
	CHECK(check_sljit_emit_fast_return(compiler, src, srcw));
	ADJUST_LOCAL_OFFSET(src, srcw);

	if (FAST_IS_REG(src))
		FAIL_IF(push_inst(compiler, OR | D(TMP_LINK) | S1(0) | S2(src), DR(TMP_LINK)));
	else if (src & SLJIT_MEM)
		FAIL_IF(emit_op_mem(compiler, WORD_DATA | LOAD_DATA, TMP_LINK, src, srcw));
	else if (src & SLJIT_IMM)
		FAIL_IF(load_immediate(compiler, TMP_LINK, srcw));

	FAIL_IF(push_inst(compiler, JMPL | D(0) | S1(TMP_LINK) | IMM(8), UNMOVABLE_INS));
	return push_inst(compiler, NOP, UNMOVABLE_INS);
}

/* --------------------------------------------------------------------- */
/*  Conditional instructions                                             */
/* --------------------------------------------------------------------- */

SLJIT_API_FUNC_ATTRIBUTE struct sljit_label* sljit_emit_label(struct sljit_compiler *compiler)
{
	struct sljit_label *label;

	CHECK_ERROR_PTR();
	CHECK_PTR(check_sljit_emit_label(compiler));

	if (compiler->last_label && compiler->last_label->size == compiler->size)
		return compiler->last_label;

	label = (struct sljit_label*)ensure_abuf(compiler, sizeof(struct sljit_label));
	PTR_FAIL_IF(!label);
	set_label(label, compiler);
	compiler->delay_slot = UNMOVABLE_INS;
	return label;
}

static sljit_ins get_cc(sljit_si type)
{
	switch (type) {
	case SLJIT_EQUAL:
	case SLJIT_MUL_NOT_OVERFLOW:
	case SLJIT_D_NOT_EQUAL: /* Unordered. */
		return DA(0x1);

	case SLJIT_NOT_EQUAL:
	case SLJIT_MUL_OVERFLOW:
	case SLJIT_D_EQUAL:
		return DA(0x9);

	case SLJIT_LESS:
	case SLJIT_D_GREATER: /* Unordered. */
		return DA(0x5);

	case SLJIT_GREATER_EQUAL:
	case SLJIT_D_LESS_EQUAL:
		return DA(0xd);

	case SLJIT_GREATER:
	case SLJIT_D_GREATER_EQUAL: /* Unordered. */
		return DA(0xc);

	case SLJIT_LESS_EQUAL:
	case SLJIT_D_LESS:
		return DA(0x4);

	case SLJIT_SIG_LESS:
		return DA(0x3);

	case SLJIT_SIG_GREATER_EQUAL:
		return DA(0xb);

	case SLJIT_SIG_GREATER:
		return DA(0xa);

	case SLJIT_SIG_LESS_EQUAL:
		return DA(0x2);

	case SLJIT_OVERFLOW:
	case SLJIT_D_UNORDERED:
		return DA(0x7);

	case SLJIT_NOT_OVERFLOW:
	case SLJIT_D_ORDERED:
		return DA(0xf);

	default:
		SLJIT_ASSERT_STOP();
		return DA(0x8);
	}
}

SLJIT_API_FUNC_ATTRIBUTE struct sljit_jump* sljit_emit_jump(struct sljit_compiler *compiler, sljit_si type)
{
	struct sljit_jump *jump;

	CHECK_ERROR_PTR();
	CHECK_PTR(check_sljit_emit_jump(compiler, type));

	jump = (struct sljit_jump*)ensure_abuf(compiler, sizeof(struct sljit_jump));
	PTR_FAIL_IF(!jump);
	set_jump(jump, compiler, type & SLJIT_REWRITABLE_JUMP);
	type &= 0xff;

	if (type < SLJIT_D_EQUAL) {
		jump->flags |= IS_COND;
		if (((compiler->delay_slot & DST_INS_MASK) != UNMOVABLE_INS) && !(compiler->delay_slot & ICC_IS_SET))
			jump->flags |= IS_MOVABLE;
#if (defined SLJIT_CONFIG_SPARC_32 && SLJIT_CONFIG_SPARC_32)
		PTR_FAIL_IF(push_inst(compiler, BICC | get_cc(type ^ 1) | 5, UNMOVABLE_INS));
#else
#error "Implementation required"
#endif
	}
	else if (type < SLJIT_JUMP) {
		jump->flags |= IS_COND;
		if (((compiler->delay_slot & DST_INS_MASK) != UNMOVABLE_INS) && !(compiler->delay_slot & FCC_IS_SET))
			jump->flags |= IS_MOVABLE;
#if (defined SLJIT_CONFIG_SPARC_32 && SLJIT_CONFIG_SPARC_32)
		PTR_FAIL_IF(push_inst(compiler, FBFCC | get_cc(type ^ 1) | 5, UNMOVABLE_INS));
#else
#error "Implementation required"
#endif
	} else {
		if ((compiler->delay_slot & DST_INS_MASK) != UNMOVABLE_INS)
			jump->flags |= IS_MOVABLE;
		if (type >= SLJIT_FAST_CALL)
			jump->flags |= IS_CALL;
	}

	PTR_FAIL_IF(emit_const(compiler, TMP_REG2, 0));
	PTR_FAIL_IF(push_inst(compiler, JMPL | D(type >= SLJIT_FAST_CALL ? TMP_LINK : 0) | S1(TMP_REG2) | IMM(0), UNMOVABLE_INS));
	jump->addr = compiler->size;
	PTR_FAIL_IF(push_inst(compiler, NOP, UNMOVABLE_INS));

	return jump;
}

SLJIT_API_FUNC_ATTRIBUTE sljit_si sljit_emit_ijump(struct sljit_compiler *compiler, sljit_si type, sljit_si src, sljit_sw srcw)
{
	struct sljit_jump *jump = NULL;
	sljit_si src_r;

	CHECK_ERROR();
	CHECK(check_sljit_emit_ijump(compiler, type, src, srcw));
	ADJUST_LOCAL_OFFSET(src, srcw);

	if (FAST_IS_REG(src))
		src_r = src;
	else if (src & SLJIT_IMM) {
		jump = (struct sljit_jump*)ensure_abuf(compiler, sizeof(struct sljit_jump));
		FAIL_IF(!jump);
		set_jump(jump, compiler, JUMP_ADDR);
		jump->u.target = srcw;
		if ((compiler->delay_slot & DST_INS_MASK) != UNMOVABLE_INS)
			jump->flags |= IS_MOVABLE;
		if (type >= SLJIT_FAST_CALL)
			jump->flags |= IS_CALL;

		FAIL_IF(emit_const(compiler, TMP_REG2, 0));
		src_r = TMP_REG2;
	}
	else {
		FAIL_IF(emit_op_mem(compiler, WORD_DATA | LOAD_DATA, TMP_REG2, src, srcw));
		src_r = TMP_REG2;
	}

	FAIL_IF(push_inst(compiler, JMPL | D(type >= SLJIT_FAST_CALL ? TMP_LINK : 0) | S1(src_r) | IMM(0), UNMOVABLE_INS));
	if (jump)
		jump->addr = compiler->size;
	return push_inst(compiler, NOP, UNMOVABLE_INS);
}

SLJIT_API_FUNC_ATTRIBUTE sljit_si sljit_emit_op_flags(struct sljit_compiler *compiler, sljit_si op,
	sljit_si dst, sljit_sw dstw,
	sljit_si src, sljit_sw srcw,
	sljit_si type)
{
	sljit_si reg, flags = (GET_FLAGS(op) ? SET_FLAGS : 0);

	CHECK_ERROR();
	CHECK(check_sljit_emit_op_flags(compiler, op, dst, dstw, src, srcw, type));
	ADJUST_LOCAL_OFFSET(dst, dstw);

	if (dst == SLJIT_UNUSED)
		return SLJIT_SUCCESS;

#if (defined SLJIT_CONFIG_SPARC_32 && SLJIT_CONFIG_SPARC_32)
	op = GET_OPCODE(op);
	reg = (op < SLJIT_ADD && FAST_IS_REG(dst)) ? dst : TMP_REG2;

	compiler->cache_arg = 0;
	compiler->cache_argw = 0;
	if (op >= SLJIT_ADD && (src & SLJIT_MEM)) {
		ADJUST_LOCAL_OFFSET(src, srcw);
		FAIL_IF(emit_op_mem2(compiler, WORD_DATA | LOAD_DATA, TMP_REG1, src, srcw, dst, dstw));
		src = TMP_REG1;
		srcw = 0;
	}

	type &= 0xff;
	if (type < SLJIT_D_EQUAL)
		FAIL_IF(push_inst(compiler, BICC | get_cc(type) | 3, UNMOVABLE_INS));
	else
		FAIL_IF(push_inst(compiler, FBFCC | get_cc(type) | 3, UNMOVABLE_INS));

	FAIL_IF(push_inst(compiler, OR | D(reg) | S1(0) | IMM(1), UNMOVABLE_INS));
	FAIL_IF(push_inst(compiler, OR | D(reg) | S1(0) | IMM(0), UNMOVABLE_INS));

	if (op >= SLJIT_ADD)
		return emit_op(compiler, op, flags | CUMULATIVE_OP | IMM_OP | ALT_KEEP_CACHE, dst, dstw, src, srcw, TMP_REG2, 0);

	return (reg == TMP_REG2) ? emit_op_mem(compiler, WORD_DATA, TMP_REG2, dst, dstw) : SLJIT_SUCCESS;
#else
#error "Implementation required"
#endif
}

SLJIT_API_FUNC_ATTRIBUTE struct sljit_const* sljit_emit_const(struct sljit_compiler *compiler, sljit_si dst, sljit_sw dstw, sljit_sw init_value)
{
	sljit_si reg;
	struct sljit_const *const_;

	CHECK_ERROR_PTR();
	CHECK_PTR(check_sljit_emit_const(compiler, dst, dstw, init_value));
	ADJUST_LOCAL_OFFSET(dst, dstw);

	const_ = (struct sljit_const*)ensure_abuf(compiler, sizeof(struct sljit_const));
	PTR_FAIL_IF(!const_);
	set_const(const_, compiler);

	reg = SLOW_IS_REG(dst) ? dst : TMP_REG2;

	PTR_FAIL_IF(emit_const(compiler, reg, init_value));

	if (dst & SLJIT_MEM)
		PTR_FAIL_IF(emit_op_mem(compiler, WORD_DATA, TMP_REG2, dst, dstw));
	return const_;
}


<|start_filename|>ext/hash/php_hash_crc32_tables.h<|end_filename|>
/*
   +----------------------------------------------------------------------+
   | PHP Version 5                                                        |
   +----------------------------------------------------------------------+
   | Copyright (c) 1997-2015 The PHP Group                                |
   +----------------------------------------------------------------------+
   | This source file is subject to version 3.01 of the PHP 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.php.net/license/3_01.txt                                  |
   | If you did not receive a copy of the PHP license and are unable to   |
   | obtain it through the world-wide-web, please send a note to          |
   | <EMAIL> so we can mail you a copy immediately.               |
   +----------------------------------------------------------------------+
   | Author: <NAME> <<EMAIL>>                               |
   +----------------------------------------------------------------------+
*/

/* $Id$ */

static const php_hash_uint32 crc32_table[] = {  0x0,
	0x04c11db7, 0x09823b6e, 0x0d4326d9, 0x130476dc, 0x17c56b6b,
	0x1a864db2, 0x1e475005, 0x2608edb8, 0x22c9f00f, 0x2f8ad6d6,
	0x2b4bcb61, 0x350c9b64, 0x31cd86d3, 0x3c8ea00a, 0x384fbdbd,
	0x4c11db70, 0x48d0c6c7, 0x4593e01e, 0x4152fda9, 0x5f15adac,
	0x5bd4b01b, 0x569796c2, 0x52568b75, 0x6a1936c8, 0x6ed82b7f,
	0x639b0da6, 0x675a1011, 0x791d4014, 0x7ddc5da3, 0x709f7b7a,
	0x745e66cd, 0x9823b6e0, 0x9ce2ab57, 0x91a18d8e, 0x95609039,
	0x8b27c03c, 0x8fe6dd8b, 0x82a5fb52, 0x8664e6e5, 0xbe2b5b58,
	0xbaea46ef, 0xb7a96036, 0xb3687d81, 0xad2f2d84, 0xa9ee3033,
	0xa4ad16ea, 0xa06c0b5d, 0xd4326d90, 0xd0f37027, 0xddb056fe,
	0xd9714b49, 0xc7361b4c, 0xc3f706fb, 0xceb42022, 0xca753d95,
	0xf23a8028, 0xf6fb9d9f, 0xfbb8bb46, 0xff79a6f1, 0xe13ef6f4,
	0xe5ffeb43, 0xe8bccd9a, 0xec7dd02d, 0x34867077, 0x30476dc0,
	0x3d044b19, 0x39c556ae, 0x278206ab, 0x23431b1c, 0x2e003dc5,
	0x2ac12072, 0x128e9dcf, 0x164f8078, 0x1b0ca6a1, 0x1fcdbb16,
	0x018aeb13, 0x054bf6a4, 0x0808d07d, 0x0cc9cdca, 0x7897ab07,
	0x7c56b6b0, 0x71159069, 0x75d48dde, 0x6b93dddb, 0x6f52c06c,
	0x6211e6b5, 0x66d0fb02, 0x5e9f46bf, 0x5a5e5b08, 0x571d7dd1,
	0x53dc6066, 0x4d9b3063, 0x495a2dd4, 0x44190b0d, 0x40d816ba,
	0xaca5c697, 0xa864db20, 0xa527fdf9, 0xa1e6e04e, 0xbfa1b04b,
	0xbb60adfc, 0xb6238b25, 0xb2e29692, 0x8aad2b2f, 0x8e6c3698,
	0x832f1041, 0x87ee0df6, 0x99a95df3, 0x9d684044, 0x902b669d,
	0x94ea7b2a, 0xe0b41de7, 0xe4750050, 0xe9362689, 0xedf73b3e,
	0xf3b06b3b, 0xf771768c, 0xfa325055, 0xfef34de2, 0xc6bcf05f,
	0xc27dede8, 0xcf3ecb31, 0xcbffd686, 0xd5b88683, 0xd1799b34,
	0xdc3abded, 0xd8fba05a, 0x690ce0ee, 0x6dcdfd59, 0x608edb80,
	0x644fc637, 0x7a089632, 0x7ec98b85, 0x738aad5c, 0x774bb0eb,
	0x4f040d56, 0x4bc510e1, 0x46863638, 0x42472b8f, 0x5c007b8a,
	0x58c1663d, 0x558240e4, 0x51435d53, 0x251d3b9e, 0x21dc2629,
	0x2c9f00f0, 0x285e1d47, 0x36194d42, 0x32d850f5, 0x3f9b762c,
	0x3b5a6b9b, 0x0315d626, 0x07d4cb91, 0x0a97ed48, 0x0e56f0ff,
	0x1011a0fa, 0x14d0bd4d, 0x19939b94, 0x1d528623, 0xf12f560e,
	0xf5ee4bb9, 0xf8ad6d60, 0xfc6c70d7, 0xe22b20d2, 0xe6ea3d65,
	0xeba91bbc, 0xef68060b, 0xd727bbb6, 0xd3e6a601, 0xdea580d8,
	0xda649d6f, 0xc423cd6a, 0xc0e2d0dd, 0xcda1f604, 0xc960ebb3,
	0xbd3e8d7e, 0xb9ff90c9, 0xb4bcb610, 0xb07daba7, 0xae3afba2,
	0xaafbe615, 0xa7b8c0cc, 0xa379dd7b, 0x9b3660c6, 0x9ff77d71,
	0x92b45ba8, 0x9675461f, 0x8832161a, 0x8cf30bad, 0x81b02d74,
	0x857130c3, 0x5d8a9099, 0x594b8d2e, 0x5408abf7, 0x50c9b640,
	0x4e8ee645, 0x4a4ffbf2, 0x470cdd2b, 0x43cdc09c, 0x7b827d21,
	0x7f436096, 0x7200464f, 0x76c15bf8, 0x68860bfd, 0x6c47164a,
	0x61043093, 0x65c52d24, 0x119b4be9, 0x155a565e, 0x18197087,
	0x1cd86d30, 0x029f3d35, 0x065e2082, 0x0b1d065b, 0x0fdc1bec,
	0x3793a651, 0x3352bbe6, 0x3e119d3f, 0x3ad08088, 0x2497d08d,
	0x2056cd3a, 0x2d15ebe3, 0x29d4f654, 0xc5a92679, 0xc1683bce,
	0xcc2b1d17, 0xc8ea00a0, 0xd6ad50a5, 0xd26c4d12, 0xdf2f6bcb,
	0xdbee767c, 0xe3a1cbc1, 0xe760d676, 0xea23f0af, 0xeee2ed18,
	0xf0a5bd1d, 0xf464a0aa, 0xf9278673, 0xfde69bc4, 0x89b8fd09,
	0x8d79e0be, 0x803ac667, 0x84fbdbd0, 0x9abc8bd5, 0x9e7d9662,
	0x933eb0bb, 0x97ffad0c, 0xafb010b1, 0xab710d06, 0xa6322bdf,
	0xa2f33668, 0xbcb4666d, 0xb8757bda, 0xb5365d03, 0xb1f740b4
};

static const php_hash_uint32 crc32b_table[] = {
	0x00000000, 0x77073096, 0xee0e612c, 0x990951ba,
	0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3,
	0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
	0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91,
	0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de,
	0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
	0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec,
	0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5,
	0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172,
	0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b,
	0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940,
	0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
	0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116,
	0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f,
	0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
	0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d,
	0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a,
	0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
	0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818,
	0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01,
	0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e,
	0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457,
	0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c,
	0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
	0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2,
	0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb,
	0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0,
	0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9,
	0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086,
	0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
	0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4,
	0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad,
	0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a,
	0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683,
	0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8,
	0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
	0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe,
	0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7,
	0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc,
	0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5,
	0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252,
	0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
	0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60,
	0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79,
	0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
	0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f,
	0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04,
	0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
	0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a,
	0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713,
	0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38,
	0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21,
	0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e,
	0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
	0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c,
	0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45,
	0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2,
	0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db,
	0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0,
	0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
	0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6,
	0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf,
	0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
	0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d,
};

/*
 * Local variables:
 * tab-width: 4
 * c-basic-offset: 4
 * End:
 * vim600: sw=4 ts=4 fdm=marker
 * vim<600: sw=4 ts=4
 */


<|start_filename|>ext/mysqlnd/mysqlnd_statistics.c<|end_filename|>
/*
  +----------------------------------------------------------------------+
  | PHP Version 5                                                        |
  +----------------------------------------------------------------------+
  | Copyright (c) 2006-2015 The PHP Group                                |
  +----------------------------------------------------------------------+
  | This source file is subject to version 3.01 of the PHP 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.php.net/license/3_01.txt                                  |
  | If you did not receive a copy of the PHP license and are unable to   |
  | obtain it through the world-wide-web, please send a note to          |
  | <EMAIL> so we can mail you a copy immediately.               |
  +----------------------------------------------------------------------+
  | Authors: <NAME> <<EMAIL>>                             |
  |          <NAME> <<EMAIL>>                           |
  |          <NAME> <<EMAIL>>                              |
  +----------------------------------------------------------------------+
*/

/* $Id$ */
#include "php.h"
#include "mysqlnd.h"
#include "mysqlnd_priv.h"
#include "mysqlnd_statistics.h"
#include "mysqlnd_debug.h"


/* {{{ mysqlnd_stats_values_names 
 */

const MYSQLND_STRING mysqlnd_stats_values_names[STAT_LAST] =
{
	{ MYSQLND_STR_W_LEN("bytes_sent") },
	{ MYSQLND_STR_W_LEN("bytes_received") }, 
	{ MYSQLND_STR_W_LEN("packets_sent") },
	{ MYSQLND_STR_W_LEN("packets_received") },
	{ MYSQLND_STR_W_LEN("protocol_overhead_in") },
	{ MYSQLND_STR_W_LEN("protocol_overhead_out") },
	{ MYSQLND_STR_W_LEN("bytes_received_ok_packet") }, 
	{ MYSQLND_STR_W_LEN("bytes_received_eof_packet") }, 
	{ MYSQLND_STR_W_LEN("bytes_received_rset_header_packet") }, 
	{ MYSQLND_STR_W_LEN("bytes_received_rset_field_meta_packet") }, 
	{ MYSQLND_STR_W_LEN("bytes_received_rset_row_packet") }, 
	{ MYSQLND_STR_W_LEN("bytes_received_prepare_response_packet") }, 
	{ MYSQLND_STR_W_LEN("bytes_received_change_user_packet") }, 
	{ MYSQLND_STR_W_LEN("packets_sent_command") }, 
	{ MYSQLND_STR_W_LEN("packets_received_ok") }, 
	{ MYSQLND_STR_W_LEN("packets_received_eof") }, 
	{ MYSQLND_STR_W_LEN("packets_received_rset_header") }, 
	{ MYSQLND_STR_W_LEN("packets_received_rset_field_meta") }, 
	{ MYSQLND_STR_W_LEN("packets_received_rset_row") }, 
	{ MYSQLND_STR_W_LEN("packets_received_prepare_response") }, 
	{ MYSQLND_STR_W_LEN("packets_received_change_user") }, 
	{ MYSQLND_STR_W_LEN("result_set_queries") },
	{ MYSQLND_STR_W_LEN("non_result_set_queries") },
	{ MYSQLND_STR_W_LEN("no_index_used") },
	{ MYSQLND_STR_W_LEN("bad_index_used") },
	{ MYSQLND_STR_W_LEN("slow_queries") },
	{ MYSQLND_STR_W_LEN("buffered_sets") },
	{ MYSQLND_STR_W_LEN("unbuffered_sets") },
	{ MYSQLND_STR_W_LEN("ps_buffered_sets") },
	{ MYSQLND_STR_W_LEN("ps_unbuffered_sets") },
	{ MYSQLND_STR_W_LEN("flushed_normal_sets") },
	{ MYSQLND_STR_W_LEN("flushed_ps_sets") },
	{ MYSQLND_STR_W_LEN("ps_prepared_never_executed") },
	{ MYSQLND_STR_W_LEN("ps_prepared_once_executed") },
	{ MYSQLND_STR_W_LEN("rows_fetched_from_server_normal") },
	{ MYSQLND_STR_W_LEN("rows_fetched_from_server_ps") },
	{ MYSQLND_STR_W_LEN("rows_buffered_from_client_normal") },
	{ MYSQLND_STR_W_LEN("rows_buffered_from_client_ps") },
	{ MYSQLND_STR_W_LEN("rows_fetched_from_client_normal_buffered") },
	{ MYSQLND_STR_W_LEN("rows_fetched_from_client_normal_unbuffered") },
	{ MYSQLND_STR_W_LEN("rows_fetched_from_client_ps_buffered") },
	{ MYSQLND_STR_W_LEN("rows_fetched_from_client_ps_unbuffered") },
	{ MYSQLND_STR_W_LEN("rows_fetched_from_client_ps_cursor") },
	{ MYSQLND_STR_W_LEN("rows_affected_normal") },
	{ MYSQLND_STR_W_LEN("rows_affected_ps") },
	{ MYSQLND_STR_W_LEN("rows_skipped_normal") },
	{ MYSQLND_STR_W_LEN("rows_skipped_ps") },
	{ MYSQLND_STR_W_LEN("copy_on_write_saved") },
	{ MYSQLND_STR_W_LEN("copy_on_write_performed") },
	{ MYSQLND_STR_W_LEN("command_buffer_too_small") },
	{ MYSQLND_STR_W_LEN("connect_success") },
	{ MYSQLND_STR_W_LEN("connect_failure") },
	{ MYSQLND_STR_W_LEN("connection_reused") },
	{ MYSQLND_STR_W_LEN("reconnect") },
	{ MYSQLND_STR_W_LEN("pconnect_success") },
	{ MYSQLND_STR_W_LEN("active_connections") },
	{ MYSQLND_STR_W_LEN("active_persistent_connections") },
	{ MYSQLND_STR_W_LEN("explicit_close") },
	{ MYSQLND_STR_W_LEN("implicit_close") },
	{ MYSQLND_STR_W_LEN("disconnect_close") },
	{ MYSQLND_STR_W_LEN("in_middle_of_command_close") },
	{ MYSQLND_STR_W_LEN("explicit_free_result") },
	{ MYSQLND_STR_W_LEN("implicit_free_result") },
	{ MYSQLND_STR_W_LEN("explicit_stmt_close") },
	{ MYSQLND_STR_W_LEN("implicit_stmt_close") },
	{ MYSQLND_STR_W_LEN("mem_emalloc_count") },
	{ MYSQLND_STR_W_LEN("mem_emalloc_amount") },
	{ MYSQLND_STR_W_LEN("mem_ecalloc_count") },
	{ MYSQLND_STR_W_LEN("mem_ecalloc_amount") },
	{ MYSQLND_STR_W_LEN("mem_erealloc_count") },
	{ MYSQLND_STR_W_LEN("mem_erealloc_amount") },
	{ MYSQLND_STR_W_LEN("mem_efree_count") },
	{ MYSQLND_STR_W_LEN("mem_efree_amount") },
	{ MYSQLND_STR_W_LEN("mem_malloc_count") },
	{ MYSQLND_STR_W_LEN("mem_malloc_amount") },
	{ MYSQLND_STR_W_LEN("mem_calloc_count") },
	{ MYSQLND_STR_W_LEN("mem_calloc_amount") },
	{ MYSQLND_STR_W_LEN("mem_realloc_count") },
	{ MYSQLND_STR_W_LEN("mem_realloc_amount") },
	{ MYSQLND_STR_W_LEN("mem_free_count") },
	{ MYSQLND_STR_W_LEN("mem_free_amount") },
	{ MYSQLND_STR_W_LEN("mem_estrndup_count") },
	{ MYSQLND_STR_W_LEN("mem_strndup_count") },
	{ MYSQLND_STR_W_LEN("mem_estndup_count") },
	{ MYSQLND_STR_W_LEN("mem_strdup_count") },
	{ MYSQLND_STR_W_LEN("proto_text_fetched_null") },
	{ MYSQLND_STR_W_LEN("proto_text_fetched_bit") },
	{ MYSQLND_STR_W_LEN("proto_text_fetched_tinyint") },
	{ MYSQLND_STR_W_LEN("proto_text_fetched_short") },
	{ MYSQLND_STR_W_LEN("proto_text_fetched_int24") },
	{ MYSQLND_STR_W_LEN("proto_text_fetched_int") },
	{ MYSQLND_STR_W_LEN("proto_text_fetched_bigint") },
	{ MYSQLND_STR_W_LEN("proto_text_fetched_decimal") },
	{ MYSQLND_STR_W_LEN("proto_text_fetched_float") },
	{ MYSQLND_STR_W_LEN("proto_text_fetched_double") },
	{ MYSQLND_STR_W_LEN("proto_text_fetched_date") },
	{ MYSQLND_STR_W_LEN("proto_text_fetched_year") },
	{ MYSQLND_STR_W_LEN("proto_text_fetched_time") },
	{ MYSQLND_STR_W_LEN("proto_text_fetched_datetime") },
	{ MYSQLND_STR_W_LEN("proto_text_fetched_timestamp") },
	{ MYSQLND_STR_W_LEN("proto_text_fetched_string") },
	{ MYSQLND_STR_W_LEN("proto_text_fetched_blob") },
	{ MYSQLND_STR_W_LEN("proto_text_fetched_enum") },
	{ MYSQLND_STR_W_LEN("proto_text_fetched_set") },
	{ MYSQLND_STR_W_LEN("proto_text_fetched_geometry") },
	{ MYSQLND_STR_W_LEN("proto_text_fetched_other") },
	{ MYSQLND_STR_W_LEN("proto_binary_fetched_null") },
	{ MYSQLND_STR_W_LEN("proto_binary_fetched_bit") },
	{ MYSQLND_STR_W_LEN("proto_binary_fetched_tinyint") },
	{ MYSQLND_STR_W_LEN("proto_binary_fetched_short") },
	{ MYSQLND_STR_W_LEN("proto_binary_fetched_int24") },
	{ MYSQLND_STR_W_LEN("proto_binary_fetched_int") },
	{ MYSQLND_STR_W_LEN("proto_binary_fetched_bigint") },
	{ MYSQLND_STR_W_LEN("proto_binary_fetched_decimal") },
	{ MYSQLND_STR_W_LEN("proto_binary_fetched_float") },
	{ MYSQLND_STR_W_LEN("proto_binary_fetched_double") },
	{ MYSQLND_STR_W_LEN("proto_binary_fetched_date") },
	{ MYSQLND_STR_W_LEN("proto_binary_fetched_year") },
	{ MYSQLND_STR_W_LEN("proto_binary_fetched_time") },
	{ MYSQLND_STR_W_LEN("proto_binary_fetched_datetime") },
	{ MYSQLND_STR_W_LEN("proto_binary_fetched_timestamp") },
	{ MYSQLND_STR_W_LEN("proto_binary_fetched_string") },
	{ MYSQLND_STR_W_LEN("proto_binary_fetched_blob") },
	{ MYSQLND_STR_W_LEN("proto_binary_fetched_enum") },
	{ MYSQLND_STR_W_LEN("proto_binary_fetched_set") },
	{ MYSQLND_STR_W_LEN("proto_binary_fetched_geometry") },
	{ MYSQLND_STR_W_LEN("proto_binary_fetched_other") },
	{ MYSQLND_STR_W_LEN("init_command_executed_count") },
	{ MYSQLND_STR_W_LEN("init_command_failed_count") },
	{ MYSQLND_STR_W_LEN("com_quit") },
	{ MYSQLND_STR_W_LEN("com_init_db") },
	{ MYSQLND_STR_W_LEN("com_query") },
	{ MYSQLND_STR_W_LEN("com_field_list") },
	{ MYSQLND_STR_W_LEN("com_create_db") },
	{ MYSQLND_STR_W_LEN("com_drop_db") },
	{ MYSQLND_STR_W_LEN("com_refresh") },
	{ MYSQLND_STR_W_LEN("com_shutdown") },
	{ MYSQLND_STR_W_LEN("com_statistics") },
	{ MYSQLND_STR_W_LEN("com_process_info") },
	{ MYSQLND_STR_W_LEN("com_connect") },
	{ MYSQLND_STR_W_LEN("com_process_kill") },
	{ MYSQLND_STR_W_LEN("com_debug") },
	{ MYSQLND_STR_W_LEN("com_ping") },
	{ MYSQLND_STR_W_LEN("com_time") },
	{ MYSQLND_STR_W_LEN("com_delayed_insert") },
	{ MYSQLND_STR_W_LEN("com_change_user") },
	{ MYSQLND_STR_W_LEN("com_binlog_dump") },
	{ MYSQLND_STR_W_LEN("com_table_dump") },
	{ MYSQLND_STR_W_LEN("com_connect_out") },
	{ MYSQLND_STR_W_LEN("com_register_slave") },
	{ MYSQLND_STR_W_LEN("com_stmt_prepare") },
	{ MYSQLND_STR_W_LEN("com_stmt_execute") },
	{ MYSQLND_STR_W_LEN("com_stmt_send_long_data") },
	{ MYSQLND_STR_W_LEN("com_stmt_close") },
	{ MYSQLND_STR_W_LEN("com_stmt_reset") },
	{ MYSQLND_STR_W_LEN("com_stmt_set_option") },
	{ MYSQLND_STR_W_LEN("com_stmt_fetch") },
	{ MYSQLND_STR_W_LEN("com_deamon") },
	{ MYSQLND_STR_W_LEN("bytes_received_real_data_normal") },
	{ MYSQLND_STR_W_LEN("bytes_received_real_data_ps") }
};
/* }}} */


/* {{{ mysqlnd_fill_stats_hash */
PHPAPI void
mysqlnd_fill_stats_hash(const MYSQLND_STATS * const stats, const MYSQLND_STRING * names, zval *return_value TSRMLS_DC ZEND_FILE_LINE_DC)
{
	unsigned int i;

	mysqlnd_array_init(return_value, stats->count);
	for (i = 0; i < stats->count; i++) {
		char tmp[25];

		sprintf((char *)&tmp, MYSQLND_LLU_SPEC, stats->values[i]);
		add_assoc_string_ex(return_value, names[i].s, names[i].l + 1, tmp, 1);
	}
}
/* }}} */


/* {{{ _mysqlnd_get_client_stats */
PHPAPI void
_mysqlnd_get_client_stats(zval *return_value TSRMLS_DC ZEND_FILE_LINE_DC)
{
	MYSQLND_STATS stats, *stats_ptr = mysqlnd_global_stats;
	DBG_ENTER("_mysqlnd_get_client_stats");
	if (!stats_ptr) {
		memset(&stats, 0, sizeof(stats));
		stats_ptr = &stats;
	}
	mysqlnd_fill_stats_hash(stats_ptr, mysqlnd_stats_values_names, return_value TSRMLS_CC ZEND_FILE_LINE_CC);
	DBG_VOID_RETURN;
}
/* }}} */


/* {{{ mysqlnd_stats_init */
PHPAPI void
mysqlnd_stats_init(MYSQLND_STATS ** stats, size_t statistic_count)
{
	*stats = calloc(1, sizeof(MYSQLND_STATS));
	if (*stats == NULL) {
		return;
	}
	(*stats)->values = calloc(statistic_count, sizeof(uint64_t));
	(*stats)->triggers = calloc(statistic_count, sizeof(mysqlnd_stat_trigger));
	(*stats)->in_trigger = FALSE;
	(*stats)->count = statistic_count;
#ifdef ZTS
	(*stats)->LOCK_access = tsrm_mutex_alloc();
#endif
}
/* }}} */


/* {{{ mysqlnd_stats_end */
PHPAPI void
mysqlnd_stats_end(MYSQLND_STATS * stats)
{
#ifdef ZTS
	tsrm_mutex_free(stats->LOCK_access);
#endif
	free(stats->triggers);
	free(stats->values);
	/* mnd_free will reference LOCK_access and crash...*/
	free(stats);
}
/* }}} */


/* {{{ mysqlnd_stats_set_trigger */
PHPAPI mysqlnd_stat_trigger
mysqlnd_stats_set_trigger(MYSQLND_STATS * const stats, enum_mysqlnd_collected_stats statistic, mysqlnd_stat_trigger trigger TSRMLS_DC)
{
	mysqlnd_stat_trigger ret = NULL;
	DBG_ENTER("mysqlnd_stats_set_trigger");
	if (stats) {
		MYSQLND_STATS_LOCK(stats);
		ret = stats->triggers[statistic];
		stats->triggers[statistic] = trigger;
		MYSQLND_STATS_UNLOCK(stats);
	}
	DBG_RETURN(ret);
}
/* }}} */


/* {{{ mysqlnd_stats_set_handler */
PHPAPI mysqlnd_stat_trigger
mysqlnd_stats_reset_triggers(MYSQLND_STATS * const stats TSRMLS_DC)
{
	mysqlnd_stat_trigger ret = NULL;
	DBG_ENTER("mysqlnd_stats_reset_trigger");
	if (stats) {
		MYSQLND_STATS_LOCK(stats);
		memset(stats->triggers, 0, stats->count * sizeof(mysqlnd_stat_trigger));
		MYSQLND_STATS_UNLOCK(stats);
	}
	DBG_RETURN(ret);
}
/* }}} */


/*
 * Local variables:
 * tab-width: 4
 * c-basic-offset: 4
 * End:
 * vim600: noet sw=4 ts=4 fdm=marker
 * vim<600: noet sw=4 ts=4
 */


<|start_filename|>ext/pcre/pcrelib/pcre_study.c<|end_filename|>
/*************************************************
*      Perl-Compatible Regular Expressions       *
*************************************************/

/* PCRE is a library of functions to support regular expressions whose syntax
and semantics are as close as possible to those of the Perl 5 language.

                       Written by <NAME>
           Copyright (c) 1997-2012 University of Cambridge

-----------------------------------------------------------------------------
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

    * Redistributions of source code must retain the above copyright notice,
      this list of conditions and the following disclaimer.

    * Redistributions in binary form must reproduce the above copyright
      notice, this list of conditions and the following disclaimer in the
      documentation and/or other materials provided with the distribution.

    * Neither the name of the University of Cambridge nor the names of its
      contributors may be used to endorse or promote products derived from
      this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
-----------------------------------------------------------------------------
*/


/* This module contains the external function pcre_study(), along with local
supporting functions. */


#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include "pcre_internal.h"

#define SET_BIT(c) start_bits[c/8] |= (1 << (c&7))

/* Returns from set_start_bits() */

enum { SSB_FAIL, SSB_DONE, SSB_CONTINUE, SSB_UNKNOWN };



/*************************************************
*   Find the minimum subject length for a group  *
*************************************************/

/* Scan a parenthesized group and compute the minimum length of subject that
is needed to match it. This is a lower bound; it does not mean there is a
string of that length that matches. In UTF8 mode, the result is in characters
rather than bytes.

Arguments:
  re              compiled pattern block
  code            pointer to start of group (the bracket)
  startcode       pointer to start of the whole pattern's code
  options         the compiling options
  recurses        chain of recurse_check to catch mutual recursion
  countptr        pointer to call count (to catch over complexity)

Returns:   the minimum length
           -1 if \C in UTF-8 mode or (*ACCEPT) was encountered
           -2 internal error (missing capturing bracket)
           -3 internal error (opcode not listed)
*/

static int
find_minlength(const REAL_PCRE *re, const pcre_uchar *code,
  const pcre_uchar *startcode, int options, recurse_check *recurses,
  int *countptr)
{
int length = -1;
/* PCRE_UTF16 has the same value as PCRE_UTF8. */
BOOL utf = (options & PCRE_UTF8) != 0;
BOOL had_recurse = FALSE;
recurse_check this_recurse;
register int branchlength = 0;
register pcre_uchar *cc = (pcre_uchar *)code + 1 + LINK_SIZE;

if ((*countptr)++ > 1000) return -1;   /* too complex */

if (*code == OP_CBRA || *code == OP_SCBRA ||
    *code == OP_CBRAPOS || *code == OP_SCBRAPOS) cc += IMM2_SIZE;

/* Scan along the opcodes for this branch. If we get to the end of the
branch, check the length against that of the other branches. */

for (;;)
  {
  int d, min;
  pcre_uchar *cs, *ce;
  register pcre_uchar op = *cc;

  switch (op)
    {
    case OP_COND:
    case OP_SCOND:

    /* If there is only one branch in a condition, the implied branch has zero
    length, so we don't add anything. This covers the DEFINE "condition"
    automatically. */

    cs = cc + GET(cc, 1);
    if (*cs != OP_ALT)
      {
      cc = cs + 1 + LINK_SIZE;
      break;
      }

    /* Otherwise we can fall through and treat it the same as any other
    subpattern. */

    case OP_CBRA:
    case OP_SCBRA:
    case OP_BRA:
    case OP_SBRA:
    case OP_CBRAPOS:
    case OP_SCBRAPOS:
    case OP_BRAPOS:
    case OP_SBRAPOS:
    case OP_ONCE:
    case OP_ONCE_NC:
    d = find_minlength(re, cc, startcode, options, recurses, countptr);
    if (d < 0) return d;
    branchlength += d;
    do cc += GET(cc, 1); while (*cc == OP_ALT);
    cc += 1 + LINK_SIZE;
    break;

    /* ACCEPT makes things far too complicated; we have to give up. */

    case OP_ACCEPT:
    case OP_ASSERT_ACCEPT:
    return -1;

    /* Reached end of a branch; if it's a ket it is the end of a nested
    call. If it's ALT it is an alternation in a nested call. If it is END it's
    the end of the outer call. All can be handled by the same code. If an
    ACCEPT was previously encountered, use the length that was in force at that
    time, and pass back the shortest ACCEPT length. */

    case OP_ALT:
    case OP_KET:
    case OP_KETRMAX:
    case OP_KETRMIN:
    case OP_KETRPOS:
    case OP_END:
    if (length < 0 || (!had_recurse && branchlength < length))
      length = branchlength;
    if (op != OP_ALT) return length;
    cc += 1 + LINK_SIZE;
    branchlength = 0;
    had_recurse = FALSE;
    break;

    /* Skip over assertive subpatterns */

    case OP_ASSERT:
    case OP_ASSERT_NOT:
    case OP_ASSERTBACK:
    case OP_ASSERTBACK_NOT:
    do cc += GET(cc, 1); while (*cc == OP_ALT);
    /* Fall through */

    /* Skip over things that don't match chars */

    case OP_REVERSE:
    case OP_CREF:
    case OP_DNCREF:
    case OP_RREF:
    case OP_DNRREF:
    case OP_DEF:
    case OP_CALLOUT:
    case OP_SOD:
    case OP_SOM:
    case OP_EOD:
    case OP_EODN:
    case OP_CIRC:
    case OP_CIRCM:
    case OP_DOLL:
    case OP_DOLLM:
    case OP_NOT_WORD_BOUNDARY:
    case OP_WORD_BOUNDARY:
    cc += PRIV(OP_lengths)[*cc];
    break;

    /* Skip over a subpattern that has a {0} or {0,x} quantifier */

    case OP_BRAZERO:
    case OP_BRAMINZERO:
    case OP_BRAPOSZERO:
    case OP_SKIPZERO:
    cc += PRIV(OP_lengths)[*cc];
    do cc += GET(cc, 1); while (*cc == OP_ALT);
    cc += 1 + LINK_SIZE;
    break;

    /* Handle literal characters and + repetitions */

    case OP_CHAR:
    case OP_CHARI:
    case OP_NOT:
    case OP_NOTI:
    case OP_PLUS:
    case OP_PLUSI:
    case OP_MINPLUS:
    case OP_MINPLUSI:
    case OP_POSPLUS:
    case OP_POSPLUSI:
    case OP_NOTPLUS:
    case OP_NOTPLUSI:
    case OP_NOTMINPLUS:
    case OP_NOTMINPLUSI:
    case OP_NOTPOSPLUS:
    case OP_NOTPOSPLUSI:
    branchlength++;
    cc += 2;
#ifdef SUPPORT_UTF
    if (utf && HAS_EXTRALEN(cc[-1])) cc += GET_EXTRALEN(cc[-1]);
#endif
    break;

    case OP_TYPEPLUS:
    case OP_TYPEMINPLUS:
    case OP_TYPEPOSPLUS:
    branchlength++;
    cc += (cc[1] == OP_PROP || cc[1] == OP_NOTPROP)? 4 : 2;
    break;

    /* Handle exact repetitions. The count is already in characters, but we
    need to skip over a multibyte character in UTF8 mode.  */

    case OP_EXACT:
    case OP_EXACTI:
    case OP_NOTEXACT:
    case OP_NOTEXACTI:
    branchlength += GET2(cc,1);
    cc += 2 + IMM2_SIZE;
#ifdef SUPPORT_UTF
    if (utf && HAS_EXTRALEN(cc[-1])) cc += GET_EXTRALEN(cc[-1]);
#endif
    break;

    case OP_TYPEEXACT:
    branchlength += GET2(cc,1);
    cc += 2 + IMM2_SIZE + ((cc[1 + IMM2_SIZE] == OP_PROP
      || cc[1 + IMM2_SIZE] == OP_NOTPROP)? 2 : 0);
    break;

    /* Handle single-char non-literal matchers */

    case OP_PROP:
    case OP_NOTPROP:
    cc += 2;
    /* Fall through */

    case OP_NOT_DIGIT:
    case OP_DIGIT:
    case OP_NOT_WHITESPACE:
    case OP_WHITESPACE:
    case OP_NOT_WORDCHAR:
    case OP_WORDCHAR:
    case OP_ANY:
    case OP_ALLANY:
    case OP_EXTUNI:
    case OP_HSPACE:
    case OP_NOT_HSPACE:
    case OP_VSPACE:
    case OP_NOT_VSPACE:
    branchlength++;
    cc++;
    break;

    /* "Any newline" might match two characters, but it also might match just
    one. */

    case OP_ANYNL:
    branchlength += 1;
    cc++;
    break;

    /* The single-byte matcher means we can't proceed in UTF-8 mode. (In
    non-UTF-8 mode \C will actually be turned into OP_ALLANY, so won't ever
    appear, but leave the code, just in case.) */

    case OP_ANYBYTE:
#ifdef SUPPORT_UTF
    if (utf) return -1;
#endif
    branchlength++;
    cc++;
    break;

    /* For repeated character types, we have to test for \p and \P, which have
    an extra two bytes of parameters. */

    case OP_TYPESTAR:
    case OP_TYPEMINSTAR:
    case OP_TYPEQUERY:
    case OP_TYPEMINQUERY:
    case OP_TYPEPOSSTAR:
    case OP_TYPEPOSQUERY:
    if (cc[1] == OP_PROP || cc[1] == OP_NOTPROP) cc += 2;
    cc += PRIV(OP_lengths)[op];
    break;

    case OP_TYPEUPTO:
    case OP_TYPEMINUPTO:
    case OP_TYPEPOSUPTO:
    if (cc[1 + IMM2_SIZE] == OP_PROP
      || cc[1 + IMM2_SIZE] == OP_NOTPROP) cc += 2;
    cc += PRIV(OP_lengths)[op];
    break;

    /* Check a class for variable quantification */

    case OP_CLASS:
    case OP_NCLASS:
#if defined SUPPORT_UTF || defined COMPILE_PCRE16 || defined COMPILE_PCRE32
    case OP_XCLASS:
    /* The original code caused an unsigned overflow in 64 bit systems,
    so now we use a conditional statement. */
    if (op == OP_XCLASS)
      cc += GET(cc, 1);
    else
      cc += PRIV(OP_lengths)[OP_CLASS];
#else
    cc += PRIV(OP_lengths)[OP_CLASS];
#endif

    switch (*cc)
      {
      case OP_CRPLUS:
      case OP_CRMINPLUS:
      case OP_CRPOSPLUS:
      branchlength++;
      /* Fall through */

      case OP_CRSTAR:
      case OP_CRMINSTAR:
      case OP_CRQUERY:
      case OP_CRMINQUERY:
      case OP_CRPOSSTAR:
      case OP_CRPOSQUERY:
      cc++;
      break;

      case OP_CRRANGE:
      case OP_CRMINRANGE:
      case OP_CRPOSRANGE:
      branchlength += GET2(cc,1);
      cc += 1 + 2 * IMM2_SIZE;
      break;

      default:
      branchlength++;
      break;
      }
    break;

    /* Backreferences and subroutine calls are treated in the same way: we find
    the minimum length for the subpattern. A recursion, however, causes an
    a flag to be set that causes the length of this branch to be ignored. The
    logic is that a recursion can only make sense if there is another
    alternation that stops the recursing. That will provide the minimum length
    (when no recursion happens). A backreference within the group that it is
    referencing behaves in the same way.

    If PCRE_JAVASCRIPT_COMPAT is set, a backreference to an unset bracket
    matches an empty string (by default it causes a matching failure), so in
    that case we must set the minimum length to zero. */

    case OP_DNREF:     /* Duplicate named pattern back reference */
    case OP_DNREFI:
    if ((options & PCRE_JAVASCRIPT_COMPAT) == 0)
      {
      int count = GET2(cc, 1+IMM2_SIZE);
      pcre_uchar *slot = (pcre_uchar *)re +
        re->name_table_offset + GET2(cc, 1) * re->name_entry_size;
      d = INT_MAX;
      while (count-- > 0)
        {
        ce = cs = (pcre_uchar *)PRIV(find_bracket)(startcode, utf, GET2(slot, 0));
        if (cs == NULL) return -2;
        do ce += GET(ce, 1); while (*ce == OP_ALT);
        if (cc > cs && cc < ce)     /* Simple recursion */
          {
          d = 0;
          had_recurse = TRUE;
          break;
          }
        else
          {
          recurse_check *r = recurses;
          for (r = recurses; r != NULL; r = r->prev) if (r->group == cs) break;
          if (r != NULL)           /* Mutual recursion */
            {
            d = 0;
            had_recurse = TRUE;
            break;
            }
          else
            {
            int dd;
            this_recurse.prev = recurses;
            this_recurse.group = cs;
            dd = find_minlength(re, cs, startcode, options, &this_recurse,
              countptr);
            if (dd < d) d = dd;
            }
          }
        slot += re->name_entry_size;
        }
      }
    else d = 0;
    cc += 1 + 2*IMM2_SIZE;
    goto REPEAT_BACK_REFERENCE;

    case OP_REF:      /* Single back reference */
    case OP_REFI:
    if ((options & PCRE_JAVASCRIPT_COMPAT) == 0)
      {
      ce = cs = (pcre_uchar *)PRIV(find_bracket)(startcode, utf, GET2(cc, 1));
      if (cs == NULL) return -2;
      do ce += GET(ce, 1); while (*ce == OP_ALT);
      if (cc > cs && cc < ce)    /* Simple recursion */
        {
        d = 0;
        had_recurse = TRUE;
        }
      else
        {
        recurse_check *r = recurses;
        for (r = recurses; r != NULL; r = r->prev) if (r->group == cs) break;
        if (r != NULL)           /* Mutual recursion */
          {
          d = 0;
          had_recurse = TRUE;
          }
        else
          {
          this_recurse.prev = recurses;
          this_recurse.group = cs;
          d = find_minlength(re, cs, startcode, options, &this_recurse,
            countptr);
          }
        }
      }
    else d = 0;
    cc += 1 + IMM2_SIZE;

    /* Handle repeated back references */

    REPEAT_BACK_REFERENCE:
    switch (*cc)
      {
      case OP_CRSTAR:
      case OP_CRMINSTAR:
      case OP_CRQUERY:
      case OP_CRMINQUERY:
      case OP_CRPOSSTAR:
      case OP_CRPOSQUERY:
      min = 0;
      cc++;
      break;

      case OP_CRPLUS:
      case OP_CRMINPLUS:
      case OP_CRPOSPLUS:
      min = 1;
      cc++;
      break;

      case OP_CRRANGE:
      case OP_CRMINRANGE:
      case OP_CRPOSRANGE:
      min = GET2(cc, 1);
      cc += 1 + 2 * IMM2_SIZE;
      break;

      default:
      min = 1;
      break;
      }

    branchlength += min * d;
    break;

    /* We can easily detect direct recursion, but not mutual recursion. This is
    caught by a recursion depth count. */

    case OP_RECURSE:
    cs = ce = (pcre_uchar *)startcode + GET(cc, 1);
    do ce += GET(ce, 1); while (*ce == OP_ALT);
    if (cc > cs && cc < ce)    /* Simple recursion */
      had_recurse = TRUE;
    else
      {
      recurse_check *r = recurses;
      for (r = recurses; r != NULL; r = r->prev) if (r->group == cs) break;
      if (r != NULL)           /* Mutual recursion */
        had_recurse = TRUE;
      else
        {
        this_recurse.prev = recurses;
        this_recurse.group = cs;
        branchlength += find_minlength(re, cs, startcode, options,
          &this_recurse, countptr);
        }
      }
    cc += 1 + LINK_SIZE;
    break;

    /* Anything else does not or need not match a character. We can get the
    item's length from the table, but for those that can match zero occurrences
    of a character, we must take special action for UTF-8 characters. As it
    happens, the "NOT" versions of these opcodes are used at present only for
    ASCII characters, so they could be omitted from this list. However, in
    future that may change, so we include them here so as not to leave a
    gotcha for a future maintainer. */

    case OP_UPTO:
    case OP_UPTOI:
    case OP_NOTUPTO:
    case OP_NOTUPTOI:
    case OP_MINUPTO:
    case OP_MINUPTOI:
    case OP_NOTMINUPTO:
    case OP_NOTMINUPTOI:
    case OP_POSUPTO:
    case OP_POSUPTOI:
    case OP_NOTPOSUPTO:
    case OP_NOTPOSUPTOI:

    case OP_STAR:
    case OP_STARI:
    case OP_NOTSTAR:
    case OP_NOTSTARI:
    case OP_MINSTAR:
    case OP_MINSTARI:
    case OP_NOTMINSTAR:
    case OP_NOTMINSTARI:
    case OP_POSSTAR:
    case OP_POSSTARI:
    case OP_NOTPOSSTAR:
    case OP_NOTPOSSTARI:

    case OP_QUERY:
    case OP_QUERYI:
    case OP_NOTQUERY:
    case OP_NOTQUERYI:
    case OP_MINQUERY:
    case OP_MINQUERYI:
    case OP_NOTMINQUERY:
    case OP_NOTMINQUERYI:
    case OP_POSQUERY:
    case OP_POSQUERYI:
    case OP_NOTPOSQUERY:
    case OP_NOTPOSQUERYI:

    cc += PRIV(OP_lengths)[op];
#ifdef SUPPORT_UTF
    if (utf && HAS_EXTRALEN(cc[-1])) cc += GET_EXTRALEN(cc[-1]);
#endif
    break;

    /* Skip these, but we need to add in the name length. */

    case OP_MARK:
    case OP_PRUNE_ARG:
    case OP_SKIP_ARG:
    case OP_THEN_ARG:
    cc += PRIV(OP_lengths)[op] + cc[1];
    break;

    /* The remaining opcodes are just skipped over. */

    case OP_CLOSE:
    case OP_COMMIT:
    case OP_FAIL:
    case OP_PRUNE:
    case OP_SET_SOM:
    case OP_SKIP:
    case OP_THEN:
    cc += PRIV(OP_lengths)[op];
    break;

    /* This should not occur: we list all opcodes explicitly so that when
    new ones get added they are properly considered. */

    default:
    return -3;
    }
  }
/* Control never gets here */
}



/*************************************************
*      Set a bit and maybe its alternate case    *
*************************************************/

/* Given a character, set its first byte's bit in the table, and also the
corresponding bit for the other version of a letter if we are caseless. In
UTF-8 mode, for characters greater than 127, we can only do the caseless thing
when Unicode property support is available.

Arguments:
  start_bits    points to the bit map
  p             points to the character
  caseless      the caseless flag
  cd            the block with char table pointers
  utf           TRUE for UTF-8 / UTF-16 / UTF-32 mode

Returns:        pointer after the character
*/

static const pcre_uchar *
set_table_bit(pcre_uint8 *start_bits, const pcre_uchar *p, BOOL caseless,
  compile_data *cd, BOOL utf)
{
pcre_uint32 c = *p;

#ifdef COMPILE_PCRE8
SET_BIT(c);

#ifdef SUPPORT_UTF
if (utf && c > 127)
  {
  GETCHARINC(c, p);
#ifdef SUPPORT_UCP
  if (caseless)
    {
    pcre_uchar buff[6];
    c = UCD_OTHERCASE(c);
    (void)PRIV(ord2utf)(c, buff);
    SET_BIT(buff[0]);
    }
#endif  /* Not SUPPORT_UCP */
  return p;
  }
#else   /* Not SUPPORT_UTF */
(void)(utf);   /* Stops warning for unused parameter */
#endif  /* SUPPORT_UTF */

/* Not UTF-8 mode, or character is less than 127. */

if (caseless && (cd->ctypes[c] & ctype_letter) != 0) SET_BIT(cd->fcc[c]);
return p + 1;
#endif  /* COMPILE_PCRE8 */

#if defined COMPILE_PCRE16 || defined COMPILE_PCRE32
if (c > 0xff)
  {
  c = 0xff;
  caseless = FALSE;
  }
SET_BIT(c);

#ifdef SUPPORT_UTF
if (utf && c > 127)
  {
  GETCHARINC(c, p);
#ifdef SUPPORT_UCP
  if (caseless)
    {
    c = UCD_OTHERCASE(c);
    if (c > 0xff)
      c = 0xff;
    SET_BIT(c);
    }
#endif  /* SUPPORT_UCP */
  return p;
  }
#else   /* Not SUPPORT_UTF */
(void)(utf);   /* Stops warning for unused parameter */
#endif  /* SUPPORT_UTF */

if (caseless && (cd->ctypes[c] & ctype_letter) != 0) SET_BIT(cd->fcc[c]);
return p + 1;
#endif
}



/*************************************************
*     Set bits for a positive character type     *
*************************************************/

/* This function sets starting bits for a character type. In UTF-8 mode, we can
only do a direct setting for bytes less than 128, as otherwise there can be
confusion with bytes in the middle of UTF-8 characters. In a "traditional"
environment, the tables will only recognize ASCII characters anyway, but in at
least one Windows environment, some higher bytes bits were set in the tables.
So we deal with that case by considering the UTF-8 encoding.

Arguments:
  start_bits     the starting bitmap
  cbit type      the type of character wanted
  table_limit    32 for non-UTF-8; 16 for UTF-8
  cd             the block with char table pointers

Returns:         nothing
*/

static void
set_type_bits(pcre_uint8 *start_bits, int cbit_type, unsigned int table_limit,
  compile_data *cd)
{
register pcre_uint32 c;
for (c = 0; c < table_limit; c++) start_bits[c] |= cd->cbits[c+cbit_type];
#if defined SUPPORT_UTF && defined COMPILE_PCRE8
if (table_limit == 32) return;
for (c = 128; c < 256; c++)
  {
  if ((cd->cbits[c/8] & (1 << (c&7))) != 0)
    {
    pcre_uchar buff[6];
    (void)PRIV(ord2utf)(c, buff);
    SET_BIT(buff[0]);
    }
  }
#endif
}


/*************************************************
*     Set bits for a negative character type     *
*************************************************/

/* This function sets starting bits for a negative character type such as \D.
In UTF-8 mode, we can only do a direct setting for bytes less than 128, as
otherwise there can be confusion with bytes in the middle of UTF-8 characters.
Unlike in the positive case, where we can set appropriate starting bits for
specific high-valued UTF-8 characters, in this case we have to set the bits for
all high-valued characters. The lowest is 0xc2, but we overkill by starting at
0xc0 (192) for simplicity.

Arguments:
  start_bits     the starting bitmap
  cbit type      the type of character wanted
  table_limit    32 for non-UTF-8; 16 for UTF-8
  cd             the block with char table pointers

Returns:         nothing
*/

static void
set_nottype_bits(pcre_uint8 *start_bits, int cbit_type, unsigned int table_limit,
  compile_data *cd)
{
register pcre_uint32 c;
for (c = 0; c < table_limit; c++) start_bits[c] |= ~cd->cbits[c+cbit_type];
#if defined SUPPORT_UTF && defined COMPILE_PCRE8
if (table_limit != 32) for (c = 24; c < 32; c++) start_bits[c] = 0xff;
#endif
}



/*************************************************
*          Create bitmap of starting bytes       *
*************************************************/

/* This function scans a compiled unanchored expression recursively and
attempts to build a bitmap of the set of possible starting bytes. As time goes
by, we may be able to get more clever at doing this. The SSB_CONTINUE return is
useful for parenthesized groups in patterns such as (a*)b where the group
provides some optional starting bytes but scanning must continue at the outer
level to find at least one mandatory byte. At the outermost level, this
function fails unless the result is SSB_DONE.

Arguments:
  code         points to an expression
  start_bits   points to a 32-byte table, initialized to 0
  utf          TRUE if in UTF-8 / UTF-16 / UTF-32 mode
  cd           the block with char table pointers

Returns:       SSB_FAIL     => Failed to find any starting bytes
               SSB_DONE     => Found mandatory starting bytes
               SSB_CONTINUE => Found optional starting bytes
               SSB_UNKNOWN  => Hit an unrecognized opcode
*/

static int
set_start_bits(const pcre_uchar *code, pcre_uint8 *start_bits, BOOL utf,
  compile_data *cd)
{
register pcre_uint32 c;
int yield = SSB_DONE;
#if defined SUPPORT_UTF && defined COMPILE_PCRE8
int table_limit = utf? 16:32;
#else
int table_limit = 32;
#endif

#if 0
/* ========================================================================= */
/* The following comment and code was inserted in January 1999. In May 2006,
when it was observed to cause compiler warnings about unused values, I took it
out again. If anybody is still using OS/2, they will have to put it back
manually. */

/* This next statement and the later reference to dummy are here in order to
trick the optimizer of the IBM C compiler for OS/2 into generating correct
code. Apparently IBM isn't going to fix the problem, and we would rather not
disable optimization (in this module it actually makes a big difference, and
the pcre module can use all the optimization it can get). */

volatile int dummy;
/* ========================================================================= */
#endif

do
  {
  BOOL try_next = TRUE;
  const pcre_uchar *tcode = code + 1 + LINK_SIZE;

  if (*code == OP_CBRA || *code == OP_SCBRA ||
      *code == OP_CBRAPOS || *code == OP_SCBRAPOS) tcode += IMM2_SIZE;

  while (try_next)    /* Loop for items in this branch */
    {
    int rc;

    switch(*tcode)
      {
      /* If we reach something we don't understand, it means a new opcode has
      been created that hasn't been added to this code. Hopefully this problem
      will be discovered during testing. */

      default:
      return SSB_UNKNOWN;

      /* Fail for a valid opcode that implies no starting bits. */

      case OP_ACCEPT:
      case OP_ASSERT_ACCEPT:
      case OP_ALLANY:
      case OP_ANY:
      case OP_ANYBYTE:
      case OP_CIRC:
      case OP_CIRCM:
      case OP_CLOSE:
      case OP_COMMIT:
      case OP_COND:
      case OP_CREF:
      case OP_DEF:
      case OP_DNCREF:
      case OP_DNREF:
      case OP_DNREFI:
      case OP_DNRREF:
      case OP_DOLL:
      case OP_DOLLM:
      case OP_END:
      case OP_EOD:
      case OP_EODN:
      case OP_EXTUNI:
      case OP_FAIL:
      case OP_MARK:
      case OP_NOT:
      case OP_NOTEXACT:
      case OP_NOTEXACTI:
      case OP_NOTI:
      case OP_NOTMINPLUS:
      case OP_NOTMINPLUSI:
      case OP_NOTMINQUERY:
      case OP_NOTMINQUERYI:
      case OP_NOTMINSTAR:
      case OP_NOTMINSTARI:
      case OP_NOTMINUPTO:
      case OP_NOTMINUPTOI:
      case OP_NOTPLUS:
      case OP_NOTPLUSI:
      case OP_NOTPOSPLUS:
      case OP_NOTPOSPLUSI:
      case OP_NOTPOSQUERY:
      case OP_NOTPOSQUERYI:
      case OP_NOTPOSSTAR:
      case OP_NOTPOSSTARI:
      case OP_NOTPOSUPTO:
      case OP_NOTPOSUPTOI:
      case OP_NOTPROP:
      case OP_NOTQUERY:
      case OP_NOTQUERYI:
      case OP_NOTSTAR:
      case OP_NOTSTARI:
      case OP_NOTUPTO:
      case OP_NOTUPTOI:
      case OP_NOT_HSPACE:
      case OP_NOT_VSPACE:
      case OP_PRUNE:
      case OP_PRUNE_ARG:
      case OP_RECURSE:
      case OP_REF:
      case OP_REFI:
      case OP_REVERSE:
      case OP_RREF:
      case OP_SCOND:
      case OP_SET_SOM:
      case OP_SKIP:
      case OP_SKIP_ARG:
      case OP_SOD:
      case OP_SOM:
      case OP_THEN:
      case OP_THEN_ARG:
      return SSB_FAIL;

      /* A "real" property test implies no starting bits, but the fake property
      PT_CLIST identifies a list of characters. These lists are short, as they
      are used for characters with more than one "other case", so there is no
      point in recognizing them for OP_NOTPROP. */

      case OP_PROP:
      if (tcode[1] != PT_CLIST) return SSB_FAIL;
        {
        const pcre_uint32 *p = PRIV(ucd_caseless_sets) + tcode[2];
        while ((c = *p++) < NOTACHAR)
          {
#if defined SUPPORT_UTF && defined COMPILE_PCRE8
          if (utf)
            {
            pcre_uchar buff[6];
            (void)PRIV(ord2utf)(c, buff);
            c = buff[0];
            }
#endif
          if (c > 0xff) SET_BIT(0xff); else SET_BIT(c);
          }
        }
      try_next = FALSE;
      break;

      /* We can ignore word boundary tests. */

      case OP_WORD_BOUNDARY:
      case OP_NOT_WORD_BOUNDARY:
      tcode++;
      break;

      /* If we hit a bracket or a positive lookahead assertion, recurse to set
      bits from within the subpattern. If it can't find anything, we have to
      give up. If it finds some mandatory character(s), we are done for this
      branch. Otherwise, carry on scanning after the subpattern. */

      case OP_BRA:
      case OP_SBRA:
      case OP_CBRA:
      case OP_SCBRA:
      case OP_BRAPOS:
      case OP_SBRAPOS:
      case OP_CBRAPOS:
      case OP_SCBRAPOS:
      case OP_ONCE:
      case OP_ONCE_NC:
      case OP_ASSERT:
      rc = set_start_bits(tcode, start_bits, utf, cd);
      if (rc == SSB_FAIL || rc == SSB_UNKNOWN) return rc;
      if (rc == SSB_DONE) try_next = FALSE; else
        {
        do tcode += GET(tcode, 1); while (*tcode == OP_ALT);
        tcode += 1 + LINK_SIZE;
        }
      break;

      /* If we hit ALT or KET, it means we haven't found anything mandatory in
      this branch, though we might have found something optional. For ALT, we
      continue with the next alternative, but we have to arrange that the final
      result from subpattern is SSB_CONTINUE rather than SSB_DONE. For KET,
      return SSB_CONTINUE: if this is the top level, that indicates failure,
      but after a nested subpattern, it causes scanning to continue. */

      case OP_ALT:
      yield = SSB_CONTINUE;
      try_next = FALSE;
      break;

      case OP_KET:
      case OP_KETRMAX:
      case OP_KETRMIN:
      case OP_KETRPOS:
      return SSB_CONTINUE;

      /* Skip over callout */

      case OP_CALLOUT:
      tcode += 2 + 2*LINK_SIZE;
      break;

      /* Skip over lookbehind and negative lookahead assertions */

      case OP_ASSERT_NOT:
      case OP_ASSERTBACK:
      case OP_ASSERTBACK_NOT:
      do tcode += GET(tcode, 1); while (*tcode == OP_ALT);
      tcode += 1 + LINK_SIZE;
      break;

      /* BRAZERO does the bracket, but carries on. */

      case OP_BRAZERO:
      case OP_BRAMINZERO:
      case OP_BRAPOSZERO:
      rc = set_start_bits(++tcode, start_bits, utf, cd);
      if (rc == SSB_FAIL || rc == SSB_UNKNOWN) return rc;
/* =========================================================================
      See the comment at the head of this function concerning the next line,
      which was an old fudge for the benefit of OS/2.
      dummy = 1;
  ========================================================================= */
      do tcode += GET(tcode,1); while (*tcode == OP_ALT);
      tcode += 1 + LINK_SIZE;
      break;

      /* SKIPZERO skips the bracket. */

      case OP_SKIPZERO:
      tcode++;
      do tcode += GET(tcode,1); while (*tcode == OP_ALT);
      tcode += 1 + LINK_SIZE;
      break;

      /* Single-char * or ? sets the bit and tries the next item */

      case OP_STAR:
      case OP_MINSTAR:
      case OP_POSSTAR:
      case OP_QUERY:
      case OP_MINQUERY:
      case OP_POSQUERY:
      tcode = set_table_bit(start_bits, tcode + 1, FALSE, cd, utf);
      break;

      case OP_STARI:
      case OP_MINSTARI:
      case OP_POSSTARI:
      case OP_QUERYI:
      case OP_MINQUERYI:
      case OP_POSQUERYI:
      tcode = set_table_bit(start_bits, tcode + 1, TRUE, cd, utf);
      break;

      /* Single-char upto sets the bit and tries the next */

      case OP_UPTO:
      case OP_MINUPTO:
      case OP_POSUPTO:
      tcode = set_table_bit(start_bits, tcode + 1 + IMM2_SIZE, FALSE, cd, utf);
      break;

      case OP_UPTOI:
      case OP_MINUPTOI:
      case OP_POSUPTOI:
      tcode = set_table_bit(start_bits, tcode + 1 + IMM2_SIZE, TRUE, cd, utf);
      break;

      /* At least one single char sets the bit and stops */

      case OP_EXACT:
      tcode += IMM2_SIZE;
      /* Fall through */
      case OP_CHAR:
      case OP_PLUS:
      case OP_MINPLUS:
      case OP_POSPLUS:
      (void)set_table_bit(start_bits, tcode + 1, FALSE, cd, utf);
      try_next = FALSE;
      break;

      case OP_EXACTI:
      tcode += IMM2_SIZE;
      /* Fall through */
      case OP_CHARI:
      case OP_PLUSI:
      case OP_MINPLUSI:
      case OP_POSPLUSI:
      (void)set_table_bit(start_bits, tcode + 1, TRUE, cd, utf);
      try_next = FALSE;
      break;

      /* Special spacing and line-terminating items. These recognize specific
      lists of characters. The difference between VSPACE and ANYNL is that the
      latter can match the two-character CRLF sequence, but that is not
      relevant for finding the first character, so their code here is
      identical. */

      case OP_HSPACE:
      SET_BIT(CHAR_HT);
      SET_BIT(CHAR_SPACE);
#ifdef SUPPORT_UTF
      if (utf)
        {
#ifdef COMPILE_PCRE8
        SET_BIT(0xC2);  /* For U+00A0 */
        SET_BIT(0xE1);  /* For U+1680, U+180E */
        SET_BIT(0xE2);  /* For U+2000 - U+200A, U+202F, U+205F */
        SET_BIT(0xE3);  /* For U+3000 */
#elif defined COMPILE_PCRE16 || defined COMPILE_PCRE32
        SET_BIT(0xA0);
        SET_BIT(0xFF);  /* For characters > 255 */
#endif  /* COMPILE_PCRE[8|16|32] */
        }
      else
#endif /* SUPPORT_UTF */
        {
#ifndef EBCDIC
        SET_BIT(0xA0);
#endif  /* Not EBCDIC */
#if defined COMPILE_PCRE16 || defined COMPILE_PCRE32
        SET_BIT(0xFF);  /* For characters > 255 */
#endif  /* COMPILE_PCRE[16|32] */
        }
      try_next = FALSE;
      break;

      case OP_ANYNL:
      case OP_VSPACE:
      SET_BIT(CHAR_LF);
      SET_BIT(CHAR_VT);
      SET_BIT(CHAR_FF);
      SET_BIT(CHAR_CR);
#ifdef SUPPORT_UTF
      if (utf)
        {
#ifdef COMPILE_PCRE8
        SET_BIT(0xC2);  /* For U+0085 */
        SET_BIT(0xE2);  /* For U+2028, U+2029 */
#elif defined COMPILE_PCRE16 || defined COMPILE_PCRE32
        SET_BIT(CHAR_NEL);
        SET_BIT(0xFF);  /* For characters > 255 */
#endif  /* COMPILE_PCRE[8|16|32] */
        }
      else
#endif /* SUPPORT_UTF */
        {
        SET_BIT(CHAR_NEL);
#if defined COMPILE_PCRE16 || defined COMPILE_PCRE32
        SET_BIT(0xFF);  /* For characters > 255 */
#endif
        }
      try_next = FALSE;
      break;

      /* Single character types set the bits and stop. Note that if PCRE_UCP
      is set, we do not see these op codes because \d etc are converted to
      properties. Therefore, these apply in the case when only characters less
      than 256 are recognized to match the types. */

      case OP_NOT_DIGIT:
      set_nottype_bits(start_bits, cbit_digit, table_limit, cd);
      try_next = FALSE;
      break;

      case OP_DIGIT:
      set_type_bits(start_bits, cbit_digit, table_limit, cd);
      try_next = FALSE;
      break;

      /* The cbit_space table has vertical tab as whitespace; we no longer
      have to play fancy tricks because Perl added VT to its whitespace at
      release 5.18. PCRE added it at release 8.34. */

      case OP_NOT_WHITESPACE:
      set_nottype_bits(start_bits, cbit_space, table_limit, cd);
      try_next = FALSE;
      break;

      case OP_WHITESPACE:
      set_type_bits(start_bits, cbit_space, table_limit, cd);
      try_next = FALSE;
      break;

      case OP_NOT_WORDCHAR:
      set_nottype_bits(start_bits, cbit_word, table_limit, cd);
      try_next = FALSE;
      break;

      case OP_WORDCHAR:
      set_type_bits(start_bits, cbit_word, table_limit, cd);
      try_next = FALSE;
      break;

      /* One or more character type fudges the pointer and restarts, knowing
      it will hit a single character type and stop there. */

      case OP_TYPEPLUS:
      case OP_TYPEMINPLUS:
      case OP_TYPEPOSPLUS:
      tcode++;
      break;

      case OP_TYPEEXACT:
      tcode += 1 + IMM2_SIZE;
      break;

      /* Zero or more repeats of character types set the bits and then
      try again. */

      case OP_TYPEUPTO:
      case OP_TYPEMINUPTO:
      case OP_TYPEPOSUPTO:
      tcode += IMM2_SIZE;  /* Fall through */

      case OP_TYPESTAR:
      case OP_TYPEMINSTAR:
      case OP_TYPEPOSSTAR:
      case OP_TYPEQUERY:
      case OP_TYPEMINQUERY:
      case OP_TYPEPOSQUERY:
      switch(tcode[1])
        {
        default:
        case OP_ANY:
        case OP_ALLANY:
        return SSB_FAIL;

        case OP_HSPACE:
        SET_BIT(CHAR_HT);
        SET_BIT(CHAR_SPACE);
#ifdef SUPPORT_UTF
        if (utf)
          {
#ifdef COMPILE_PCRE8
          SET_BIT(0xC2);  /* For U+00A0 */
          SET_BIT(0xE1);  /* For U+1680, U+180E */
          SET_BIT(0xE2);  /* For U+2000 - U+200A, U+202F, U+205F */
          SET_BIT(0xE3);  /* For U+3000 */
#elif defined COMPILE_PCRE16 || defined COMPILE_PCRE32
          SET_BIT(0xA0);
          SET_BIT(0xFF);  /* For characters > 255 */
#endif  /* COMPILE_PCRE[8|16|32] */
          }
        else
#endif /* SUPPORT_UTF */
#ifndef EBCDIC
          SET_BIT(0xA0);
#endif  /* Not EBCDIC */
        break;

        case OP_ANYNL:
        case OP_VSPACE:
        SET_BIT(CHAR_LF);
        SET_BIT(CHAR_VT);
        SET_BIT(CHAR_FF);
        SET_BIT(CHAR_CR);
#ifdef SUPPORT_UTF
        if (utf)
          {
#ifdef COMPILE_PCRE8
          SET_BIT(0xC2);  /* For U+0085 */
          SET_BIT(0xE2);  /* For U+2028, U+2029 */
#elif defined COMPILE_PCRE16 || defined COMPILE_PCRE32
          SET_BIT(CHAR_NEL);
          SET_BIT(0xFF);  /* For characters > 255 */
#endif  /* COMPILE_PCRE16 */
          }
        else
#endif /* SUPPORT_UTF */
          SET_BIT(CHAR_NEL);
        break;

        case OP_NOT_DIGIT:
        set_nottype_bits(start_bits, cbit_digit, table_limit, cd);
        break;

        case OP_DIGIT:
        set_type_bits(start_bits, cbit_digit, table_limit, cd);
        break;

        /* The cbit_space table has vertical tab as whitespace; we no longer
        have to play fancy tricks because Perl added VT to its whitespace at
        release 5.18. PCRE added it at release 8.34. */

        case OP_NOT_WHITESPACE:
        set_nottype_bits(start_bits, cbit_space, table_limit, cd);
        break;

        case OP_WHITESPACE:
        set_type_bits(start_bits, cbit_space, table_limit, cd);
        break;

        case OP_NOT_WORDCHAR:
        set_nottype_bits(start_bits, cbit_word, table_limit, cd);
        break;

        case OP_WORDCHAR:
        set_type_bits(start_bits, cbit_word, table_limit, cd);
        break;
        }

      tcode += 2;
      break;

      /* Character class where all the information is in a bit map: set the
      bits and either carry on or not, according to the repeat count. If it was
      a negative class, and we are operating with UTF-8 characters, any byte
      with a value >= 0xc4 is a potentially valid starter because it starts a
      character with a value > 255. */

#if defined SUPPORT_UTF || !defined COMPILE_PCRE8
      case OP_XCLASS:
      if ((tcode[1 + LINK_SIZE] & XCL_HASPROP) != 0)
        return SSB_FAIL;
      /* All bits are set. */
      if ((tcode[1 + LINK_SIZE] & XCL_MAP) == 0 && (tcode[1 + LINK_SIZE] & XCL_NOT) != 0)
        return SSB_FAIL;
#endif
      /* Fall through */

      case OP_NCLASS:
#if defined SUPPORT_UTF && defined COMPILE_PCRE8
      if (utf)
        {
        start_bits[24] |= 0xf0;              /* Bits for 0xc4 - 0xc8 */
        memset(start_bits+25, 0xff, 7);      /* Bits for 0xc9 - 0xff */
        }
#endif
#if defined COMPILE_PCRE16 || defined COMPILE_PCRE32
      SET_BIT(0xFF);                         /* For characters > 255 */
#endif
      /* Fall through */

      case OP_CLASS:
        {
        pcre_uint8 *map;
#if defined SUPPORT_UTF || !defined COMPILE_PCRE8
        map = NULL;
        if (*tcode == OP_XCLASS)
          {
          if ((tcode[1 + LINK_SIZE] & XCL_MAP) != 0)
            map = (pcre_uint8 *)(tcode + 1 + LINK_SIZE + 1);
          tcode += GET(tcode, 1);
          }
        else
#endif
          {
          tcode++;
          map = (pcre_uint8 *)tcode;
          tcode += 32 / sizeof(pcre_uchar);
          }

        /* In UTF-8 mode, the bits in a bit map correspond to character
        values, not to byte values. However, the bit map we are constructing is
        for byte values. So we have to do a conversion for characters whose
        value is > 127. In fact, there are only two possible starting bytes for
        characters in the range 128 - 255. */

#if defined SUPPORT_UTF || !defined COMPILE_PCRE8
        if (map != NULL)
#endif
          {
#if defined SUPPORT_UTF && defined COMPILE_PCRE8
          if (utf)
            {
            for (c = 0; c < 16; c++) start_bits[c] |= map[c];
            for (c = 128; c < 256; c++)
              {
              if ((map[c/8] && (1 << (c&7))) != 0)
                {
                int d = (c >> 6) | 0xc0;            /* Set bit for this starter */
                start_bits[d/8] |= (1 << (d&7));    /* and then skip on to the */
                c = (c & 0xc0) + 0x40 - 1;          /* next relevant character. */
                }
              }
            }
          else
#endif
            {
            /* In non-UTF-8 mode, the two bit maps are completely compatible. */
            for (c = 0; c < 32; c++) start_bits[c] |= map[c];
            }
          }

        /* Advance past the bit map, and act on what follows. For a zero
        minimum repeat, continue; otherwise stop processing. */

        switch (*tcode)
          {
          case OP_CRSTAR:
          case OP_CRMINSTAR:
          case OP_CRQUERY:
          case OP_CRMINQUERY:
          case OP_CRPOSSTAR:
          case OP_CRPOSQUERY:
          tcode++;
          break;

          case OP_CRRANGE:
          case OP_CRMINRANGE:
          case OP_CRPOSRANGE:
          if (GET2(tcode, 1) == 0) tcode += 1 + 2 * IMM2_SIZE;
            else try_next = FALSE;
          break;

          default:
          try_next = FALSE;
          break;
          }
        }
      break; /* End of bitmap class handling */

      }      /* End of switch */
    }        /* End of try_next loop */

  code += GET(code, 1);   /* Advance to next branch */
  }
while (*code == OP_ALT);
return yield;
}





/*************************************************
*          Study a compiled expression           *
*************************************************/

/* This function is handed a compiled expression that it must study to produce
information that will speed up the matching. It returns a pcre[16]_extra block
which then gets handed back to pcre_exec().

Arguments:
  re        points to the compiled expression
  options   contains option bits
  errorptr  points to where to place error messages;
            set NULL unless error

Returns:    pointer to a pcre[16]_extra block, with study_data filled in and
              the appropriate flags set;
            NULL on error or if no optimization possible
*/

#if defined COMPILE_PCRE8
PCRE_EXP_DEFN pcre_extra * PCRE_CALL_CONVENTION
pcre_study(const pcre *external_re, int options, const char **errorptr)
#elif defined COMPILE_PCRE16
PCRE_EXP_DEFN pcre16_extra * PCRE_CALL_CONVENTION
pcre16_study(const pcre16 *external_re, int options, const char **errorptr)
#elif defined COMPILE_PCRE32
PCRE_EXP_DEFN pcre32_extra * PCRE_CALL_CONVENTION
pcre32_study(const pcre32 *external_re, int options, const char **errorptr)
#endif
{
int min;
int count = 0;
BOOL bits_set = FALSE;
pcre_uint8 start_bits[32];
PUBL(extra) *extra = NULL;
pcre_study_data *study;
const pcre_uint8 *tables;
pcre_uchar *code;
compile_data compile_block;
const REAL_PCRE *re = (const REAL_PCRE *)external_re;


*errorptr = NULL;

if (re == NULL || re->magic_number != MAGIC_NUMBER)
  {
  *errorptr = "argument is not a compiled regular expression";
  return NULL;
  }

if ((re->flags & PCRE_MODE) == 0)
  {
#if defined COMPILE_PCRE8
  *errorptr = "argument not compiled in 8 bit mode";
#elif defined COMPILE_PCRE16
  *errorptr = "argument not compiled in 16 bit mode";
#elif defined COMPILE_PCRE32
  *errorptr = "argument not compiled in 32 bit mode";
#endif
  return NULL;
  }

if ((options & ~PUBLIC_STUDY_OPTIONS) != 0)
  {
  *errorptr = "unknown or incorrect option bit(s) set";
  return NULL;
  }

code = (pcre_uchar *)re + re->name_table_offset +
  (re->name_count * re->name_entry_size);

/* For an anchored pattern, or an unanchored pattern that has a first char, or
a multiline pattern that matches only at "line starts", there is no point in
seeking a list of starting bytes. */

if ((re->options & PCRE_ANCHORED) == 0 &&
    (re->flags & (PCRE_FIRSTSET|PCRE_STARTLINE)) == 0)
  {
  int rc;

  /* Set the character tables in the block that is passed around */

  tables = re->tables;

#if defined COMPILE_PCRE8
  if (tables == NULL)
    (void)pcre_fullinfo(external_re, NULL, PCRE_INFO_DEFAULT_TABLES,
    (void *)(&tables));
#elif defined COMPILE_PCRE16
  if (tables == NULL)
    (void)pcre16_fullinfo(external_re, NULL, PCRE_INFO_DEFAULT_TABLES,
    (void *)(&tables));
#elif defined COMPILE_PCRE32
  if (tables == NULL)
    (void)pcre32_fullinfo(external_re, NULL, PCRE_INFO_DEFAULT_TABLES,
    (void *)(&tables));
#endif

  compile_block.lcc = tables + lcc_offset;
  compile_block.fcc = tables + fcc_offset;
  compile_block.cbits = tables + cbits_offset;
  compile_block.ctypes = tables + ctypes_offset;

  /* See if we can find a fixed set of initial characters for the pattern. */

  memset(start_bits, 0, 32 * sizeof(pcre_uint8));
  rc = set_start_bits(code, start_bits, (re->options & PCRE_UTF8) != 0,
    &compile_block);
  bits_set = rc == SSB_DONE;
  if (rc == SSB_UNKNOWN)
    {
    *errorptr = "internal error: opcode not recognized";
    return NULL;
    }
  }

/* Find the minimum length of subject string. */

switch(min = find_minlength(re, code, code, re->options, NULL, &count))
  {
  case -2: *errorptr = "internal error: missing capturing bracket"; return NULL;
  case -3: *errorptr = "internal error: opcode not recognized"; return NULL;
  default: break;
  }

/* If a set of starting bytes has been identified, or if the minimum length is
greater than zero, or if JIT optimization has been requested, or if
PCRE_STUDY_EXTRA_NEEDED is set, get a pcre[16]_extra block and a
pcre_study_data block. The study data is put in the latter, which is pointed to
by the former, which may also get additional data set later by the calling
program. At the moment, the size of pcre_study_data is fixed. We nevertheless
save it in a field for returning via the pcre_fullinfo() function so that if it
becomes variable in the future, we don't have to change that code. */

if (bits_set || min > 0 || (options & (
#ifdef SUPPORT_JIT
    PCRE_STUDY_JIT_COMPILE | PCRE_STUDY_JIT_PARTIAL_SOFT_COMPILE |
    PCRE_STUDY_JIT_PARTIAL_HARD_COMPILE |
#endif
    PCRE_STUDY_EXTRA_NEEDED)) != 0)
  {
  extra = (PUBL(extra) *)(PUBL(malloc))
    (sizeof(PUBL(extra)) + sizeof(pcre_study_data));
  if (extra == NULL)
    {
    *errorptr = "failed to get memory";
    return NULL;
    }

  study = (pcre_study_data *)((char *)extra + sizeof(PUBL(extra)));
  extra->flags = PCRE_EXTRA_STUDY_DATA;
  extra->study_data = study;

  study->size = sizeof(pcre_study_data);
  study->flags = 0;

  /* Set the start bits always, to avoid unset memory errors if the
  study data is written to a file, but set the flag only if any of the bits
  are set, to save time looking when none are. */

  if (bits_set)
    {
    study->flags |= PCRE_STUDY_MAPPED;
    memcpy(study->start_bits, start_bits, sizeof(start_bits));
    }
  else memset(study->start_bits, 0, 32 * sizeof(pcre_uint8));

#ifdef PCRE_DEBUG
  if (bits_set)
    {
    pcre_uint8 *ptr = start_bits;
    int i;

    printf("Start bits:\n");
    for (i = 0; i < 32; i++)
      printf("%3d: %02x%s", i * 8, *ptr++, ((i + 1) & 0x7) != 0? " " : "\n");
    }
#endif

  /* Always set the minlength value in the block, because the JIT compiler
  makes use of it. However, don't set the bit unless the length is greater than
  zero - the interpretive pcre_exec() and pcre_dfa_exec() needn't waste time
  checking the zero case. */

  if (min > 0)
    {
    study->flags |= PCRE_STUDY_MINLEN;
    study->minlength = min;
    }
  else study->minlength = 0;

  /* If JIT support was compiled and requested, attempt the JIT compilation.
  If no starting bytes were found, and the minimum length is zero, and JIT
  compilation fails, abandon the extra block and return NULL, unless
  PCRE_STUDY_EXTRA_NEEDED is set. */

#ifdef SUPPORT_JIT
  extra->executable_jit = NULL;
  if ((options & PCRE_STUDY_JIT_COMPILE) != 0)
    PRIV(jit_compile)(re, extra, JIT_COMPILE);
  if ((options & PCRE_STUDY_JIT_PARTIAL_SOFT_COMPILE) != 0)
    PRIV(jit_compile)(re, extra, JIT_PARTIAL_SOFT_COMPILE);
  if ((options & PCRE_STUDY_JIT_PARTIAL_HARD_COMPILE) != 0)
    PRIV(jit_compile)(re, extra, JIT_PARTIAL_HARD_COMPILE);

  if (study->flags == 0 && (extra->flags & PCRE_EXTRA_EXECUTABLE_JIT) == 0 &&
      (options & PCRE_STUDY_EXTRA_NEEDED) == 0)
    {
#if defined COMPILE_PCRE8
    pcre_free_study(extra);
#elif defined COMPILE_PCRE16
    pcre16_free_study(extra);
#elif defined COMPILE_PCRE32
    pcre32_free_study(extra);
#endif
    extra = NULL;
    }
#endif
  }

return extra;
}


/*************************************************
*          Free the study data                   *
*************************************************/

/* This function frees the memory that was obtained by pcre_study().

Argument:   a pointer to the pcre[16]_extra block
Returns:    nothing
*/

#if defined COMPILE_PCRE8
PCRE_EXP_DEFN void
pcre_free_study(pcre_extra *extra)
#elif defined COMPILE_PCRE16
PCRE_EXP_DEFN void
pcre16_free_study(pcre16_extra *extra)
#elif defined COMPILE_PCRE32
PCRE_EXP_DEFN void
pcre32_free_study(pcre32_extra *extra)
#endif
{
if (extra == NULL)
  return;
#ifdef SUPPORT_JIT
if ((extra->flags & PCRE_EXTRA_EXECUTABLE_JIT) != 0 &&
     extra->executable_jit != NULL)
  PRIV(jit_free)(extra->executable_jit);
#endif
PUBL(free)(extra);
}

/* End of pcre_study.c */


<|start_filename|>ext/pcre/pcrelib/sljit/sljitNativeARM_32.c<|end_filename|>
/*
 *    Stack-less Just-In-Time compiler
 *
 *    Copyright 2009-2012 <NAME> (<EMAIL>). All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification, are
 * permitted provided that the following conditions are met:
 *
 *   1. Redistributions of source code must retain the above copyright notice, this list of
 *      conditions and the following disclaimer.
 *
 *   2. Redistributions in binary form must reproduce the above copyright notice, this list
 *      of conditions and the following disclaimer in the documentation and/or other materials
 *      provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) AND CONTRIBUTORS ``AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
 * SHALL THE COPYRIGHT HOLDER(S) OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
 * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

SLJIT_API_FUNC_ATTRIBUTE SLJIT_CONST char* sljit_get_platform_name(void)
{
#if (defined SLJIT_CONFIG_ARM_V7 && SLJIT_CONFIG_ARM_V7)
	return "ARMv7" SLJIT_CPUINFO;
#elif (defined SLJIT_CONFIG_ARM_V5 && SLJIT_CONFIG_ARM_V5)
	return "ARMv5" SLJIT_CPUINFO;
#else
#error "Internal error: Unknown ARM architecture"
#endif
}

/* Last register + 1. */
#define TMP_REG1	(SLJIT_NUMBER_OF_REGISTERS + 2)
#define TMP_REG2	(SLJIT_NUMBER_OF_REGISTERS + 3)
#define TMP_REG3	(SLJIT_NUMBER_OF_REGISTERS + 4)
#define TMP_PC		(SLJIT_NUMBER_OF_REGISTERS + 5)

#define TMP_FREG1	(0)
#define TMP_FREG2	(SLJIT_NUMBER_OF_FLOAT_REGISTERS + 1)

/* In ARM instruction words.
   Cache lines are usually 32 byte aligned. */
#define CONST_POOL_ALIGNMENT	8
#define CONST_POOL_EMPTY	0xffffffff

#define ALIGN_INSTRUCTION(ptr) \
	(sljit_uw*)(((sljit_uw)(ptr) + (CONST_POOL_ALIGNMENT * sizeof(sljit_uw)) - 1) & ~((CONST_POOL_ALIGNMENT * sizeof(sljit_uw)) - 1))
#define MAX_DIFFERENCE(max_diff) \
	(((max_diff) / (sljit_si)sizeof(sljit_uw)) - (CONST_POOL_ALIGNMENT - 1))

/* See sljit_emit_enter and sljit_emit_op0 if you want to change them. */
static SLJIT_CONST sljit_ub reg_map[SLJIT_NUMBER_OF_REGISTERS + 6] = {
	0, 0, 1, 2, 11, 10, 9, 8, 7, 6, 5, 4, 13, 3, 12, 14, 15
};

#define RM(rm) (reg_map[rm])
#define RD(rd) (reg_map[rd] << 12)
#define RN(rn) (reg_map[rn] << 16)

/* --------------------------------------------------------------------- */
/*  Instrucion forms                                                     */
/* --------------------------------------------------------------------- */

/* The instruction includes the AL condition.
   INST_NAME - CONDITIONAL remove this flag. */
#define COND_MASK	0xf0000000
#define CONDITIONAL	0xe0000000
#define PUSH_POOL	0xff000000

/* DP - Data Processing instruction (use with EMIT_DATA_PROCESS_INS). */
#define ADC_DP		0x5
#define ADD_DP		0x4
#define AND_DP		0x0
#define B		0xea000000
#define BIC_DP		0xe
#define BL		0xeb000000
#define BLX		0xe12fff30
#define BX		0xe12fff10
#define CLZ		0xe16f0f10
#define CMP_DP		0xa
#define BKPT		0xe1200070
#define EOR_DP		0x1
#define MOV_DP		0xd
#define MUL		0xe0000090
#define MVN_DP		0xf
#define NOP		0xe1a00000
#define ORR_DP		0xc
#define PUSH		0xe92d0000
#define POP		0xe8bd0000
#define RSB_DP		0x3
#define RSC_DP		0x7
#define SBC_DP		0x6
#define SMULL		0xe0c00090
#define SUB_DP		0x2
#define UMULL		0xe0800090
#define VABS_F32	0xeeb00ac0
#define VADD_F32	0xee300a00
#define VCMP_F32	0xeeb40a40
#define VCVT_F32_S32	0xeeb80ac0
#define VCVT_F64_F32	0xeeb70ac0
#define VCVT_S32_F32	0xeebd0ac0
#define VDIV_F32	0xee800a00
#define VMOV_F32	0xeeb00a40
#define VMOV		0xee000a10
#define VMRS		0xeef1fa10
#define VMUL_F32	0xee200a00
#define VNEG_F32	0xeeb10a40
#define VSTR_F32	0xed000a00
#define VSUB_F32	0xee300a40

#if (defined SLJIT_CONFIG_ARM_V7 && SLJIT_CONFIG_ARM_V7)
/* Arm v7 specific instructions. */
#define MOVW		0xe3000000
#define MOVT		0xe3400000
#define SXTB		0xe6af0070
#define SXTH		0xe6bf0070
#define UXTB		0xe6ef0070
#define UXTH		0xe6ff0070
#endif

#if (defined SLJIT_CONFIG_ARM_V5 && SLJIT_CONFIG_ARM_V5)

static sljit_si push_cpool(struct sljit_compiler *compiler)
{
	/* Pushing the constant pool into the instruction stream. */
	sljit_uw* inst;
	sljit_uw* cpool_ptr;
	sljit_uw* cpool_end;
	sljit_si i;

	/* The label could point the address after the constant pool. */
	if (compiler->last_label && compiler->last_label->size == compiler->size)
		compiler->last_label->size += compiler->cpool_fill + (CONST_POOL_ALIGNMENT - 1) + 1;

	SLJIT_ASSERT(compiler->cpool_fill > 0 && compiler->cpool_fill <= CPOOL_SIZE);
	inst = (sljit_uw*)ensure_buf(compiler, sizeof(sljit_uw));
	FAIL_IF(!inst);
	compiler->size++;
	*inst = 0xff000000 | compiler->cpool_fill;

	for (i = 0; i < CONST_POOL_ALIGNMENT - 1; i++) {
		inst = (sljit_uw*)ensure_buf(compiler, sizeof(sljit_uw));
		FAIL_IF(!inst);
		compiler->size++;
		*inst = 0;
	}

	cpool_ptr = compiler->cpool;
	cpool_end = cpool_ptr + compiler->cpool_fill;
	while (cpool_ptr < cpool_end) {
		inst = (sljit_uw*)ensure_buf(compiler, sizeof(sljit_uw));
		FAIL_IF(!inst);
		compiler->size++;
		*inst = *cpool_ptr++;
	}
	compiler->cpool_diff = CONST_POOL_EMPTY;
	compiler->cpool_fill = 0;
	return SLJIT_SUCCESS;
}

static sljit_si push_inst(struct sljit_compiler *compiler, sljit_uw inst)
{
	sljit_uw* ptr;

	if (SLJIT_UNLIKELY(compiler->cpool_diff != CONST_POOL_EMPTY && compiler->size - compiler->cpool_diff >= MAX_DIFFERENCE(4092)))
		FAIL_IF(push_cpool(compiler));

	ptr = (sljit_uw*)ensure_buf(compiler, sizeof(sljit_uw));
	FAIL_IF(!ptr);
	compiler->size++;
	*ptr = inst;
	return SLJIT_SUCCESS;
}

static sljit_si push_inst_with_literal(struct sljit_compiler *compiler, sljit_uw inst, sljit_uw literal)
{
	sljit_uw* ptr;
	sljit_uw cpool_index = CPOOL_SIZE;
	sljit_uw* cpool_ptr;
	sljit_uw* cpool_end;
	sljit_ub* cpool_unique_ptr;

	if (SLJIT_UNLIKELY(compiler->cpool_diff != CONST_POOL_EMPTY && compiler->size - compiler->cpool_diff >= MAX_DIFFERENCE(4092)))
		FAIL_IF(push_cpool(compiler));
	else if (compiler->cpool_fill > 0) {
		cpool_ptr = compiler->cpool;
		cpool_end = cpool_ptr + compiler->cpool_fill;
		cpool_unique_ptr = compiler->cpool_unique;
		do {
			if ((*cpool_ptr == literal) && !(*cpool_unique_ptr)) {
				cpool_index = cpool_ptr - compiler->cpool;
				break;
			}
			cpool_ptr++;
			cpool_unique_ptr++;
		} while (cpool_ptr < cpool_end);
	}

	if (cpool_index == CPOOL_SIZE) {
		/* Must allocate a new entry in the literal pool. */
		if (compiler->cpool_fill < CPOOL_SIZE) {
			cpool_index = compiler->cpool_fill;
			compiler->cpool_fill++;
		}
		else {
			FAIL_IF(push_cpool(compiler));
			cpool_index = 0;
			compiler->cpool_fill = 1;
		}
	}

	SLJIT_ASSERT((inst & 0xfff) == 0);
	ptr = (sljit_uw*)ensure_buf(compiler, sizeof(sljit_uw));
	FAIL_IF(!ptr);
	compiler->size++;
	*ptr = inst | cpool_index;

	compiler->cpool[cpool_index] = literal;
	compiler->cpool_unique[cpool_index] = 0;
	if (compiler->cpool_diff == CONST_POOL_EMPTY)
		compiler->cpool_diff = compiler->size;
	return SLJIT_SUCCESS;
}

static sljit_si push_inst_with_unique_literal(struct sljit_compiler *compiler, sljit_uw inst, sljit_uw literal)
{
	sljit_uw* ptr;
	if (SLJIT_UNLIKELY((compiler->cpool_diff != CONST_POOL_EMPTY && compiler->size - compiler->cpool_diff >= MAX_DIFFERENCE(4092)) || compiler->cpool_fill >= CPOOL_SIZE))
		FAIL_IF(push_cpool(compiler));

	SLJIT_ASSERT(compiler->cpool_fill < CPOOL_SIZE && (inst & 0xfff) == 0);
	ptr = (sljit_uw*)ensure_buf(compiler, sizeof(sljit_uw));
	FAIL_IF(!ptr);
	compiler->size++;
	*ptr = inst | compiler->cpool_fill;

	compiler->cpool[compiler->cpool_fill] = literal;
	compiler->cpool_unique[compiler->cpool_fill] = 1;
	compiler->cpool_fill++;
	if (compiler->cpool_diff == CONST_POOL_EMPTY)
		compiler->cpool_diff = compiler->size;
	return SLJIT_SUCCESS;
}

static SLJIT_INLINE sljit_si prepare_blx(struct sljit_compiler *compiler)
{
	/* Place for at least two instruction (doesn't matter whether the first has a literal). */
	if (SLJIT_UNLIKELY(compiler->cpool_diff != CONST_POOL_EMPTY && compiler->size - compiler->cpool_diff >= MAX_DIFFERENCE(4088)))
		return push_cpool(compiler);
	return SLJIT_SUCCESS;
}

static SLJIT_INLINE sljit_si emit_blx(struct sljit_compiler *compiler)
{
	/* Must follow tightly the previous instruction (to be able to convert it to bl instruction). */
	SLJIT_ASSERT(compiler->cpool_diff == CONST_POOL_EMPTY || compiler->size - compiler->cpool_diff < MAX_DIFFERENCE(4092));
	return push_inst(compiler, BLX | RM(TMP_REG1));
}

static sljit_uw patch_pc_relative_loads(sljit_uw *last_pc_patch, sljit_uw *code_ptr, sljit_uw* const_pool, sljit_uw cpool_size)
{
	sljit_uw diff;
	sljit_uw ind;
	sljit_uw counter = 0;
	sljit_uw* clear_const_pool = const_pool;
	sljit_uw* clear_const_pool_end = const_pool + cpool_size;

	SLJIT_ASSERT(const_pool - code_ptr <= CONST_POOL_ALIGNMENT);
	/* Set unused flag for all literals in the constant pool.
	   I.e.: unused literals can belong to branches, which can be encoded as B or BL.
	   We can "compress" the constant pool by discarding these literals. */
	while (clear_const_pool < clear_const_pool_end)
		*clear_const_pool++ = (sljit_uw)(-1);

	while (last_pc_patch < code_ptr) {
		/* Data transfer instruction with Rn == r15. */
		if ((*last_pc_patch & 0x0c0f0000) == 0x040f0000) {
			diff = const_pool - last_pc_patch;
			ind = (*last_pc_patch) & 0xfff;

			/* Must be a load instruction with immediate offset. */
			SLJIT_ASSERT(ind < cpool_size && !(*last_pc_patch & (1 << 25)) && (*last_pc_patch & (1 << 20)));
			if ((sljit_si)const_pool[ind] < 0) {
				const_pool[ind] = counter;
				ind = counter;
				counter++;
			}
			else
				ind = const_pool[ind];

			SLJIT_ASSERT(diff >= 1);
			if (diff >= 2 || ind > 0) {
				diff = (diff + ind - 2) << 2;
				SLJIT_ASSERT(diff <= 0xfff);
				*last_pc_patch = (*last_pc_patch & ~0xfff) | diff;
			}
			else
				*last_pc_patch = (*last_pc_patch & ~(0xfff | (1 << 23))) | 0x004;
		}
		last_pc_patch++;
	}
	return counter;
}

/* In some rare ocasions we may need future patches. The probability is close to 0 in practice. */
struct future_patch {
	struct future_patch* next;
	sljit_si index;
	sljit_si value;
};

static sljit_si resolve_const_pool_index(struct sljit_compiler *compiler, struct future_patch **first_patch, sljit_uw cpool_current_index, sljit_uw *cpool_start_address, sljit_uw *buf_ptr)
{
	sljit_si value;
	struct future_patch *curr_patch, *prev_patch;

	SLJIT_UNUSED_ARG(compiler);

	/* Using the values generated by patch_pc_relative_loads. */
	if (!*first_patch)
		value = (sljit_si)cpool_start_address[cpool_current_index];
	else {
		curr_patch = *first_patch;
		prev_patch = 0;
		while (1) {
			if (!curr_patch) {
				value = (sljit_si)cpool_start_address[cpool_current_index];
				break;
			}
			if ((sljit_uw)curr_patch->index == cpool_current_index) {
				value = curr_patch->value;
				if (prev_patch)
					prev_patch->next = curr_patch->next;
				else
					*first_patch = curr_patch->next;
				SLJIT_FREE(curr_patch, compiler->allocator_data);
				break;
			}
			prev_patch = curr_patch;
			curr_patch = curr_patch->next;
		}
	}

	if (value >= 0) {
		if ((sljit_uw)value > cpool_current_index) {
			curr_patch = (struct future_patch*)SLJIT_MALLOC(sizeof(struct future_patch), compiler->allocator_data);
			if (!curr_patch) {
				while (*first_patch) {
					curr_patch = *first_patch;
					*first_patch = (*first_patch)->next;
					SLJIT_FREE(curr_patch, compiler->allocator_data);
				}
				return SLJIT_ERR_ALLOC_FAILED;
			}
			curr_patch->next = *first_patch;
			curr_patch->index = value;
			curr_patch->value = cpool_start_address[value];
			*first_patch = curr_patch;
		}
		cpool_start_address[value] = *buf_ptr;
	}
	return SLJIT_SUCCESS;
}

#else

static sljit_si push_inst(struct sljit_compiler *compiler, sljit_uw inst)
{
	sljit_uw* ptr;

	ptr = (sljit_uw*)ensure_buf(compiler, sizeof(sljit_uw));
	FAIL_IF(!ptr);
	compiler->size++;
	*ptr = inst;
	return SLJIT_SUCCESS;
}

static SLJIT_INLINE sljit_si emit_imm(struct sljit_compiler *compiler, sljit_si reg, sljit_sw imm)
{
	FAIL_IF(push_inst(compiler, MOVW | RD(reg) | ((imm << 4) & 0xf0000) | (imm & 0xfff)));
	return push_inst(compiler, MOVT | RD(reg) | ((imm >> 12) & 0xf0000) | ((imm >> 16) & 0xfff));
}

#endif

static SLJIT_INLINE sljit_si detect_jump_type(struct sljit_jump *jump, sljit_uw *code_ptr, sljit_uw *code)
{
	sljit_sw diff;

	if (jump->flags & SLJIT_REWRITABLE_JUMP)
		return 0;

#if (defined SLJIT_CONFIG_ARM_V5 && SLJIT_CONFIG_ARM_V5)
	if (jump->flags & IS_BL)
		code_ptr--;

	if (jump->flags & JUMP_ADDR)
		diff = ((sljit_sw)jump->u.target - (sljit_sw)(code_ptr + 2));
	else {
		SLJIT_ASSERT(jump->flags & JUMP_LABEL);
		diff = ((sljit_sw)(code + jump->u.label->size) - (sljit_sw)(code_ptr + 2));
	}

	/* Branch to Thumb code has not been optimized yet. */
	if (diff & 0x3)
		return 0;

	if (jump->flags & IS_BL) {
		if (diff <= 0x01ffffff && diff >= -0x02000000) {
			*code_ptr = (BL - CONDITIONAL) | (*(code_ptr + 1) & COND_MASK);
			jump->flags |= PATCH_B;
			return 1;
		}
	}
	else {
		if (diff <= 0x01ffffff && diff >= -0x02000000) {
			*code_ptr = (B - CONDITIONAL) | (*code_ptr & COND_MASK);
			jump->flags |= PATCH_B;
		}
	}
#else
	if (jump->flags & JUMP_ADDR)
		diff = ((sljit_sw)jump->u.target - (sljit_sw)code_ptr);
	else {
		SLJIT_ASSERT(jump->flags & JUMP_LABEL);
		diff = ((sljit_sw)(code + jump->u.label->size) - (sljit_sw)code_ptr);
	}

	/* Branch to Thumb code has not been optimized yet. */
	if (diff & 0x3)
		return 0;

	if (diff <= 0x01ffffff && diff >= -0x02000000) {
		code_ptr -= 2;
		*code_ptr = ((jump->flags & IS_BL) ? (BL - CONDITIONAL) : (B - CONDITIONAL)) | (code_ptr[2] & COND_MASK);
		jump->flags |= PATCH_B;
		return 1;
	}
#endif
	return 0;
}

static SLJIT_INLINE void inline_set_jump_addr(sljit_uw addr, sljit_uw new_addr, sljit_si flush)
{
#if (defined SLJIT_CONFIG_ARM_V5 && SLJIT_CONFIG_ARM_V5)
	sljit_uw *ptr = (sljit_uw*)addr;
	sljit_uw *inst = (sljit_uw*)ptr[0];
	sljit_uw mov_pc = ptr[1];
	sljit_si bl = (mov_pc & 0x0000f000) != RD(TMP_PC);
	sljit_sw diff = (sljit_sw)(((sljit_sw)new_addr - (sljit_sw)(inst + 2)) >> 2);

	if (diff <= 0x7fffff && diff >= -0x800000) {
		/* Turn to branch. */
		if (!bl) {
			inst[0] = (mov_pc & COND_MASK) | (B - CONDITIONAL) | (diff & 0xffffff);
			if (flush) {
				SLJIT_CACHE_FLUSH(inst, inst + 1);
			}
		} else {
			inst[0] = (mov_pc & COND_MASK) | (BL - CONDITIONAL) | (diff & 0xffffff);
			inst[1] = NOP;
			if (flush) {
				SLJIT_CACHE_FLUSH(inst, inst + 2);
			}
		}
	} else {
		/* Get the position of the constant. */
		if (mov_pc & (1 << 23))
			ptr = inst + ((mov_pc & 0xfff) >> 2) + 2;
		else
			ptr = inst + 1;

		if (*inst != mov_pc) {
			inst[0] = mov_pc;
			if (!bl) {
				if (flush) {
					SLJIT_CACHE_FLUSH(inst, inst + 1);
				}
			} else {
				inst[1] = BLX | RM(TMP_REG1);
				if (flush) {
					SLJIT_CACHE_FLUSH(inst, inst + 2);
				}
			}
		}
		*ptr = new_addr;
	}
#else
	sljit_uw *inst = (sljit_uw*)addr;
	SLJIT_ASSERT((inst[0] & 0xfff00000) == MOVW && (inst[1] & 0xfff00000) == MOVT);
	inst[0] = MOVW | (inst[0] & 0xf000) | ((new_addr << 4) & 0xf0000) | (new_addr & 0xfff);
	inst[1] = MOVT | (inst[1] & 0xf000) | ((new_addr >> 12) & 0xf0000) | ((new_addr >> 16) & 0xfff);
	if (flush) {
		SLJIT_CACHE_FLUSH(inst, inst + 2);
	}
#endif
}

static sljit_uw get_imm(sljit_uw imm);

static SLJIT_INLINE void inline_set_const(sljit_uw addr, sljit_sw new_constant, sljit_si flush)
{
#if (defined SLJIT_CONFIG_ARM_V5 && SLJIT_CONFIG_ARM_V5)
	sljit_uw *ptr = (sljit_uw*)addr;
	sljit_uw *inst = (sljit_uw*)ptr[0];
	sljit_uw ldr_literal = ptr[1];
	sljit_uw src2;

	src2 = get_imm(new_constant);
	if (src2) {
		*inst = 0xe3a00000 | (ldr_literal & 0xf000) | src2;
		if (flush) {
			SLJIT_CACHE_FLUSH(inst, inst + 1);
		}
		return;
	}

	src2 = get_imm(~new_constant);
	if (src2) {
		*inst = 0xe3e00000 | (ldr_literal & 0xf000) | src2;
		if (flush) {
			SLJIT_CACHE_FLUSH(inst, inst + 1);
		}
		return;
	}

	if (ldr_literal & (1 << 23))
		ptr = inst + ((ldr_literal & 0xfff) >> 2) + 2;
	else
		ptr = inst + 1;

	if (*inst != ldr_literal) {
		*inst = ldr_literal;
		if (flush) {
			SLJIT_CACHE_FLUSH(inst, inst + 1);
		}
	}
	*ptr = new_constant;
#else
	sljit_uw *inst = (sljit_uw*)addr;
	SLJIT_ASSERT((inst[0] & 0xfff00000) == MOVW && (inst[1] & 0xfff00000) == MOVT);
	inst[0] = MOVW | (inst[0] & 0xf000) | ((new_constant << 4) & 0xf0000) | (new_constant & 0xfff);
	inst[1] = MOVT | (inst[1] & 0xf000) | ((new_constant >> 12) & 0xf0000) | ((new_constant >> 16) & 0xfff);
	if (flush) {
		SLJIT_CACHE_FLUSH(inst, inst + 2);
	}
#endif
}

SLJIT_API_FUNC_ATTRIBUTE void* sljit_generate_code(struct sljit_compiler *compiler)
{
	struct sljit_memory_fragment *buf;
	sljit_uw *code;
	sljit_uw *code_ptr;
	sljit_uw *buf_ptr;
	sljit_uw *buf_end;
	sljit_uw size;
	sljit_uw word_count;
#if (defined SLJIT_CONFIG_ARM_V5 && SLJIT_CONFIG_ARM_V5)
	sljit_uw cpool_size;
	sljit_uw cpool_skip_alignment;
	sljit_uw cpool_current_index;
	sljit_uw *cpool_start_address;
	sljit_uw *last_pc_patch;
	struct future_patch *first_patch;
#endif

	struct sljit_label *label;
	struct sljit_jump *jump;
	struct sljit_const *const_;

	CHECK_ERROR_PTR();
	CHECK_PTR(check_sljit_generate_code(compiler));
	reverse_buf(compiler);

	/* Second code generation pass. */
#if (defined SLJIT_CONFIG_ARM_V5 && SLJIT_CONFIG_ARM_V5)
	size = compiler->size + (compiler->patches << 1);
	if (compiler->cpool_fill > 0)
		size += compiler->cpool_fill + CONST_POOL_ALIGNMENT - 1;
#else
	size = compiler->size;
#endif
	code = (sljit_uw*)SLJIT_MALLOC_EXEC(size * sizeof(sljit_uw));
	PTR_FAIL_WITH_EXEC_IF(code);
	buf = compiler->buf;

#if (defined SLJIT_CONFIG_ARM_V5 && SLJIT_CONFIG_ARM_V5)
	cpool_size = 0;
	cpool_skip_alignment = 0;
	cpool_current_index = 0;
	cpool_start_address = NULL;
	first_patch = NULL;
	last_pc_patch = code;
#endif

	code_ptr = code;
	word_count = 0;

	label = compiler->labels;
	jump = compiler->jumps;
	const_ = compiler->consts;

	if (label && label->size == 0) {
		label->addr = (sljit_uw)code;
		label->size = 0;
		label = label->next;
	}

	do {
		buf_ptr = (sljit_uw*)buf->memory;
		buf_end = buf_ptr + (buf->used_size >> 2);
		do {
			word_count++;
#if (defined SLJIT_CONFIG_ARM_V5 && SLJIT_CONFIG_ARM_V5)
			if (cpool_size > 0) {
				if (cpool_skip_alignment > 0) {
					buf_ptr++;
					cpool_skip_alignment--;
				}
				else {
					if (SLJIT_UNLIKELY(resolve_const_pool_index(compiler, &first_patch, cpool_current_index, cpool_start_address, buf_ptr))) {
						SLJIT_FREE_EXEC(code);
						compiler->error = SLJIT_ERR_ALLOC_FAILED;
						return NULL;
					}
					buf_ptr++;
					if (++cpool_current_index >= cpool_size) {
						SLJIT_ASSERT(!first_patch);
						cpool_size = 0;
						if (label && label->size == word_count) {
							/* Points after the current instruction. */
							label->addr = (sljit_uw)code_ptr;
							label->size = code_ptr - code;
							label = label->next;
						}
					}
				}
			}
			else if ((*buf_ptr & 0xff000000) != PUSH_POOL) {
#endif
				*code_ptr = *buf_ptr++;
				/* These structures are ordered by their address. */
				SLJIT_ASSERT(!label || label->size >= word_count);
				SLJIT_ASSERT(!jump || jump->addr >= word_count);
				SLJIT_ASSERT(!const_ || const_->addr >= word_count);
				if (jump && jump->addr == word_count) {
#if (defined SLJIT_CONFIG_ARM_V5 && SLJIT_CONFIG_ARM_V5)
					if (detect_jump_type(jump, code_ptr, code))
						code_ptr--;
					jump->addr = (sljit_uw)code_ptr;
#else
					jump->addr = (sljit_uw)(code_ptr - 2);
					if (detect_jump_type(jump, code_ptr, code))
						code_ptr -= 2;
#endif
					jump = jump->next;
				}
				if (label && label->size == word_count) {
					/* code_ptr can be affected above. */
					label->addr = (sljit_uw)(code_ptr + 1);
					label->size = (code_ptr + 1) - code;
					label = label->next;
				}
				if (const_ && const_->addr == word_count) {
#if (defined SLJIT_CONFIG_ARM_V5 && SLJIT_CONFIG_ARM_V5)
					const_->addr = (sljit_uw)code_ptr;
#else
					const_->addr = (sljit_uw)(code_ptr - 1);
#endif
					const_ = const_->next;
				}
				code_ptr++;
#if (defined SLJIT_CONFIG_ARM_V5 && SLJIT_CONFIG_ARM_V5)
			}
			else {
				/* Fortunately, no need to shift. */
				cpool_size = *buf_ptr++ & ~PUSH_POOL;
				SLJIT_ASSERT(cpool_size > 0);
				cpool_start_address = ALIGN_INSTRUCTION(code_ptr + 1);
				cpool_current_index = patch_pc_relative_loads(last_pc_patch, code_ptr, cpool_start_address, cpool_size);
				if (cpool_current_index > 0) {
					/* Unconditional branch. */
					*code_ptr = B | (((cpool_start_address - code_ptr) + cpool_current_index - 2) & ~PUSH_POOL);
					code_ptr = cpool_start_address + cpool_current_index;
				}
				cpool_skip_alignment = CONST_POOL_ALIGNMENT - 1;
				cpool_current_index = 0;
				last_pc_patch = code_ptr;
			}
#endif
		} while (buf_ptr < buf_end);
		buf = buf->next;
	} while (buf);

	SLJIT_ASSERT(!label);
	SLJIT_ASSERT(!jump);
	SLJIT_ASSERT(!const_);

#if (defined SLJIT_CONFIG_ARM_V5 && SLJIT_CONFIG_ARM_V5)
	SLJIT_ASSERT(cpool_size == 0);
	if (compiler->cpool_fill > 0) {
		cpool_start_address = ALIGN_INSTRUCTION(code_ptr);
		cpool_current_index = patch_pc_relative_loads(last_pc_patch, code_ptr, cpool_start_address, compiler->cpool_fill);
		if (cpool_current_index > 0)
			code_ptr = cpool_start_address + cpool_current_index;

		buf_ptr = compiler->cpool;
		buf_end = buf_ptr + compiler->cpool_fill;
		cpool_current_index = 0;
		while (buf_ptr < buf_end) {
			if (SLJIT_UNLIKELY(resolve_const_pool_index(compiler, &first_patch, cpool_current_index, cpool_start_address, buf_ptr))) {
				SLJIT_FREE_EXEC(code);
				compiler->error = SLJIT_ERR_ALLOC_FAILED;
				return NULL;
			}
			buf_ptr++;
			cpool_current_index++;
		}
		SLJIT_ASSERT(!first_patch);
	}
#endif

	jump = compiler->jumps;
	while (jump) {
		buf_ptr = (sljit_uw*)jump->addr;

		if (jump->flags & PATCH_B) {
			if (!(jump->flags & JUMP_ADDR)) {
				SLJIT_ASSERT(jump->flags & JUMP_LABEL);
				SLJIT_ASSERT(((sljit_sw)jump->u.label->addr - (sljit_sw)(buf_ptr + 2)) <= 0x01ffffff && ((sljit_sw)jump->u.label->addr - (sljit_sw)(buf_ptr + 2)) >= -0x02000000);
				*buf_ptr |= (((sljit_sw)jump->u.label->addr - (sljit_sw)(buf_ptr + 2)) >> 2) & 0x00ffffff;
			}
			else {
				SLJIT_ASSERT(((sljit_sw)jump->u.target - (sljit_sw)(buf_ptr + 2)) <= 0x01ffffff && ((sljit_sw)jump->u.target - (sljit_sw)(buf_ptr + 2)) >= -0x02000000);
				*buf_ptr |= (((sljit_sw)jump->u.target - (sljit_sw)(buf_ptr + 2)) >> 2) & 0x00ffffff;
			}
		}
		else if (jump->flags & SLJIT_REWRITABLE_JUMP) {
#if (defined SLJIT_CONFIG_ARM_V5 && SLJIT_CONFIG_ARM_V5)
			jump->addr = (sljit_uw)code_ptr;
			code_ptr[0] = (sljit_uw)buf_ptr;
			code_ptr[1] = *buf_ptr;
			inline_set_jump_addr((sljit_uw)code_ptr, (jump->flags & JUMP_LABEL) ? jump->u.label->addr : jump->u.target, 0);
			code_ptr += 2;
#else
			inline_set_jump_addr((sljit_uw)buf_ptr, (jump->flags & JUMP_LABEL) ? jump->u.label->addr : jump->u.target, 0);
#endif
		}
		else {
#if (defined SLJIT_CONFIG_ARM_V5 && SLJIT_CONFIG_ARM_V5)
			if (jump->flags & IS_BL)
				buf_ptr--;
			if (*buf_ptr & (1 << 23))
				buf_ptr += ((*buf_ptr & 0xfff) >> 2) + 2;
			else
				buf_ptr += 1;
			*buf_ptr = (jump->flags & JUMP_LABEL) ? jump->u.label->addr : jump->u.target;
#else
			inline_set_jump_addr((sljit_uw)buf_ptr, (jump->flags & JUMP_LABEL) ? jump->u.label->addr : jump->u.target, 0);
#endif
		}
		jump = jump->next;
	}

#if (defined SLJIT_CONFIG_ARM_V5 && SLJIT_CONFIG_ARM_V5)
	const_ = compiler->consts;
	while (const_) {
		buf_ptr = (sljit_uw*)const_->addr;
		const_->addr = (sljit_uw)code_ptr;

		code_ptr[0] = (sljit_uw)buf_ptr;
		code_ptr[1] = *buf_ptr;
		if (*buf_ptr & (1 << 23))
			buf_ptr += ((*buf_ptr & 0xfff) >> 2) + 2;
		else
			buf_ptr += 1;
		/* Set the value again (can be a simple constant). */
		inline_set_const((sljit_uw)code_ptr, *buf_ptr, 0);
		code_ptr += 2;

		const_ = const_->next;
	}
#endif

	SLJIT_ASSERT(code_ptr - code <= (sljit_si)size);

	compiler->error = SLJIT_ERR_COMPILED;
	compiler->executable_size = (code_ptr - code) * sizeof(sljit_uw);
	SLJIT_CACHE_FLUSH(code, code_ptr);
	return code;
}

/* --------------------------------------------------------------------- */
/*  Entry, exit                                                          */
/* --------------------------------------------------------------------- */

/* emit_op inp_flags.
   WRITE_BACK must be the first, since it is a flag. */
#define WRITE_BACK	0x01
#define ALLOW_IMM	0x02
#define ALLOW_INV_IMM	0x04
#define ALLOW_ANY_IMM	(ALLOW_IMM | ALLOW_INV_IMM)
#define ARG_TEST	0x08

/* Creates an index in data_transfer_insts array. */
#define WORD_DATA	0x00
#define BYTE_DATA	0x10
#define HALF_DATA	0x20
#define SIGNED_DATA	0x40
#define LOAD_DATA	0x80

/* Condition: AL. */
#define EMIT_DATA_PROCESS_INS(opcode, set_flags, dst, src1, src2) \
	(0xe0000000 | ((opcode) << 21) | (set_flags) | RD(dst) | RN(src1) | (src2))

static sljit_si emit_op(struct sljit_compiler *compiler, sljit_si op, sljit_si inp_flags,
	sljit_si dst, sljit_sw dstw,
	sljit_si src1, sljit_sw src1w,
	sljit_si src2, sljit_sw src2w);

SLJIT_API_FUNC_ATTRIBUTE sljit_si sljit_emit_enter(struct sljit_compiler *compiler,
	sljit_si options, sljit_si args, sljit_si scratches, sljit_si saveds,
	sljit_si fscratches, sljit_si fsaveds, sljit_si local_size)
{
	sljit_si size, i, tmp;
	sljit_uw push;

	CHECK_ERROR();
	CHECK(check_sljit_emit_enter(compiler, options, args, scratches, saveds, fscratches, fsaveds, local_size));
	set_emit_enter(compiler, options, args, scratches, saveds, fscratches, fsaveds, local_size);

	/* Push saved registers, temporary registers
	   stmdb sp!, {..., lr} */
	push = PUSH | (1 << 14);

	tmp = saveds < SLJIT_NUMBER_OF_SAVED_REGISTERS ? (SLJIT_S0 + 1 - saveds) : SLJIT_FIRST_SAVED_REG;
	for (i = SLJIT_S0; i >= tmp; i--)
		push |= 1 << reg_map[i];

	for (i = scratches; i >= SLJIT_FIRST_SAVED_REG; i--)
		push |= 1 << reg_map[i];

	FAIL_IF(push_inst(compiler, push));

	/* Stack must be aligned to 8 bytes: */
	size = GET_SAVED_REGISTERS_SIZE(scratches, saveds, 1);
	local_size = ((size + local_size + 7) & ~7) - size;
	compiler->local_size = local_size;
	if (local_size > 0)
		FAIL_IF(emit_op(compiler, SLJIT_SUB, ALLOW_IMM, SLJIT_SP, 0, SLJIT_SP, 0, SLJIT_IMM, local_size));

	if (args >= 1)
		FAIL_IF(push_inst(compiler, EMIT_DATA_PROCESS_INS(MOV_DP, 0, SLJIT_S0, SLJIT_UNUSED, RM(SLJIT_R0))));
	if (args >= 2)
		FAIL_IF(push_inst(compiler, EMIT_DATA_PROCESS_INS(MOV_DP, 0, SLJIT_S1, SLJIT_UNUSED, RM(SLJIT_R1))));
	if (args >= 3)
		FAIL_IF(push_inst(compiler, EMIT_DATA_PROCESS_INS(MOV_DP, 0, SLJIT_S2, SLJIT_UNUSED, RM(SLJIT_R2))));

	return SLJIT_SUCCESS;
}

SLJIT_API_FUNC_ATTRIBUTE sljit_si sljit_set_context(struct sljit_compiler *compiler,
	sljit_si options, sljit_si args, sljit_si scratches, sljit_si saveds,
	sljit_si fscratches, sljit_si fsaveds, sljit_si local_size)
{
	sljit_si size;

	CHECK_ERROR();
	CHECK(check_sljit_set_context(compiler, options, args, scratches, saveds, fscratches, fsaveds, local_size));
	set_set_context(compiler, options, args, scratches, saveds, fscratches, fsaveds, local_size);

	size = GET_SAVED_REGISTERS_SIZE(scratches, saveds, 1);
	compiler->local_size = ((size + local_size + 7) & ~7) - size;
	return SLJIT_SUCCESS;
}

SLJIT_API_FUNC_ATTRIBUTE sljit_si sljit_emit_return(struct sljit_compiler *compiler, sljit_si op, sljit_si src, sljit_sw srcw)
{
	sljit_si i, tmp;
	sljit_uw pop;

	CHECK_ERROR();
	CHECK(check_sljit_emit_return(compiler, op, src, srcw));

	FAIL_IF(emit_mov_before_return(compiler, op, src, srcw));

	if (compiler->local_size > 0)
		FAIL_IF(emit_op(compiler, SLJIT_ADD, ALLOW_IMM, SLJIT_SP, 0, SLJIT_SP, 0, SLJIT_IMM, compiler->local_size));

	/* Push saved registers, temporary registers
	   ldmia sp!, {..., pc} */
	pop = POP | (1 << 15);

	tmp = compiler->saveds < SLJIT_NUMBER_OF_SAVED_REGISTERS ? (SLJIT_S0 + 1 - compiler->saveds) : SLJIT_FIRST_SAVED_REG;
	for (i = SLJIT_S0; i >= tmp; i--)
		pop |= 1 << reg_map[i];

	for (i = compiler->scratches; i >= SLJIT_FIRST_SAVED_REG; i--)
		pop |= 1 << reg_map[i];

	return push_inst(compiler, pop);
}

/* --------------------------------------------------------------------- */
/*  Operators                                                            */
/* --------------------------------------------------------------------- */

/* s/l - store/load (1 bit)
   u/s - signed/unsigned (1 bit)
   w/b/h/N - word/byte/half/NOT allowed (2 bit)
   It contans 16 items, but not all are different. */

static sljit_sw data_transfer_insts[16] = {
/* s u w */ 0xe5000000 /* str */,
/* s u b */ 0xe5400000 /* strb */,
/* s u h */ 0xe10000b0 /* strh */,
/* s u N */ 0x00000000 /* not allowed */,
/* s s w */ 0xe5000000 /* str */,
/* s s b */ 0xe5400000 /* strb */,
/* s s h */ 0xe10000b0 /* strh */,
/* s s N */ 0x00000000 /* not allowed */,

/* l u w */ 0xe5100000 /* ldr */,
/* l u b */ 0xe5500000 /* ldrb */,
/* l u h */ 0xe11000b0 /* ldrh */,
/* l u N */ 0x00000000 /* not allowed */,
/* l s w */ 0xe5100000 /* ldr */,
/* l s b */ 0xe11000d0 /* ldrsb */,
/* l s h */ 0xe11000f0 /* ldrsh */,
/* l s N */ 0x00000000 /* not allowed */,
};

#define EMIT_DATA_TRANSFER(type, add, wb, target, base1, base2) \
	(data_transfer_insts[(type) >> 4] | ((add) << 23) | ((wb) << 21) | (reg_map[target] << 12) | (reg_map[base1] << 16) | (base2))
/* Normal ldr/str instruction.
   Type2: ldrsb, ldrh, ldrsh */
#define IS_TYPE1_TRANSFER(type) \
	(data_transfer_insts[(type) >> 4] & 0x04000000)
#define TYPE2_TRANSFER_IMM(imm) \
	(((imm) & 0xf) | (((imm) & 0xf0) << 4) | (1 << 22))

/* flags: */
  /* Arguments are swapped. */
#define ARGS_SWAPPED	0x01
  /* Inverted immediate. */
#define INV_IMM		0x02
  /* Source and destination is register. */
#define REG_DEST	0x04
#define REG_SOURCE	0x08
  /* One instruction is enough. */
#define FAST_DEST	0x10
  /* Multiple instructions are required. */
#define SLOW_DEST	0x20
/* SET_FLAGS must be (1 << 20) as it is also the value of S bit (can be used for optimization). */
#define SET_FLAGS	(1 << 20)
/* dst: reg
   src1: reg
   src2: reg or imm (if allowed)
   SRC2_IMM must be (1 << 25) as it is also the value of I bit (can be used for optimization). */
#define SRC2_IMM	(1 << 25)

#define EMIT_DATA_PROCESS_INS_AND_RETURN(opcode) \
	return push_inst(compiler, EMIT_DATA_PROCESS_INS(opcode, flags & SET_FLAGS, dst, src1, (src2 & SRC2_IMM) ? src2 : RM(src2)))

#define EMIT_FULL_DATA_PROCESS_INS_AND_RETURN(opcode, dst, src1, src2) \
	return push_inst(compiler, EMIT_DATA_PROCESS_INS(opcode, flags & SET_FLAGS, dst, src1, src2))

#define EMIT_SHIFT_INS_AND_RETURN(opcode) \
	SLJIT_ASSERT(!(flags & INV_IMM) && !(src2 & SRC2_IMM)); \
	if (compiler->shift_imm != 0x20) { \
		SLJIT_ASSERT(src1 == TMP_REG1); \
		SLJIT_ASSERT(!(flags & ARGS_SWAPPED)); \
		if (compiler->shift_imm != 0) \
			return push_inst(compiler, EMIT_DATA_PROCESS_INS(MOV_DP, flags & SET_FLAGS, dst, SLJIT_UNUSED, (compiler->shift_imm << 7) | (opcode << 5) | reg_map[src2])); \
		return push_inst(compiler, EMIT_DATA_PROCESS_INS(MOV_DP, flags & SET_FLAGS, dst, SLJIT_UNUSED, reg_map[src2])); \
	} \
	return push_inst(compiler, EMIT_DATA_PROCESS_INS(MOV_DP, flags & SET_FLAGS, dst, SLJIT_UNUSED, (reg_map[(flags & ARGS_SWAPPED) ? src1 : src2] << 8) | (opcode << 5) | 0x10 | ((flags & ARGS_SWAPPED) ? reg_map[src2] : reg_map[src1])));

static SLJIT_INLINE sljit_si emit_single_op(struct sljit_compiler *compiler, sljit_si op, sljit_si flags,
	sljit_si dst, sljit_si src1, sljit_si src2)
{
	sljit_sw mul_inst;

	switch (GET_OPCODE(op)) {
	case SLJIT_MOV:
		SLJIT_ASSERT(src1 == TMP_REG1 && !(flags & ARGS_SWAPPED));
		if (dst != src2) {
			if (src2 & SRC2_IMM) {
				if (flags & INV_IMM)
					EMIT_FULL_DATA_PROCESS_INS_AND_RETURN(MVN_DP, dst, SLJIT_UNUSED, src2);
				EMIT_FULL_DATA_PROCESS_INS_AND_RETURN(MOV_DP, dst, SLJIT_UNUSED, src2);
			}
			EMIT_FULL_DATA_PROCESS_INS_AND_RETURN(MOV_DP, dst, SLJIT_UNUSED, reg_map[src2]);
		}
		return SLJIT_SUCCESS;

	case SLJIT_MOV_UB:
	case SLJIT_MOV_SB:
		SLJIT_ASSERT(src1 == TMP_REG1 && !(flags & ARGS_SWAPPED));
		if ((flags & (REG_DEST | REG_SOURCE)) == (REG_DEST | REG_SOURCE)) {
#if (defined SLJIT_CONFIG_ARM_V5 && SLJIT_CONFIG_ARM_V5)
			if (op == SLJIT_MOV_UB)
				return push_inst(compiler, EMIT_DATA_PROCESS_INS(AND_DP, 0, dst, src2, SRC2_IMM | 0xff));
			FAIL_IF(push_inst(compiler, EMIT_DATA_PROCESS_INS(MOV_DP, 0, dst, SLJIT_UNUSED, (24 << 7) | reg_map[src2])));
			return push_inst(compiler, EMIT_DATA_PROCESS_INS(MOV_DP, 0, dst, SLJIT_UNUSED, (24 << 7) | (op == SLJIT_MOV_UB ? 0x20 : 0x40) | reg_map[dst]));
#else
			return push_inst(compiler, (op == SLJIT_MOV_UB ? UXTB : SXTB) | RD(dst) | RM(src2));
#endif
		}
		else if (dst != src2) {
			SLJIT_ASSERT(src2 & SRC2_IMM);
			if (flags & INV_IMM)
				EMIT_FULL_DATA_PROCESS_INS_AND_RETURN(MVN_DP, dst, SLJIT_UNUSED, src2);
			EMIT_FULL_DATA_PROCESS_INS_AND_RETURN(MOV_DP, dst, SLJIT_UNUSED, src2);
		}
		return SLJIT_SUCCESS;

	case SLJIT_MOV_UH:
	case SLJIT_MOV_SH:
		SLJIT_ASSERT(src1 == TMP_REG1 && !(flags & ARGS_SWAPPED));
		if ((flags & (REG_DEST | REG_SOURCE)) == (REG_DEST | REG_SOURCE)) {
#if (defined SLJIT_CONFIG_ARM_V5 && SLJIT_CONFIG_ARM_V5)
			FAIL_IF(push_inst(compiler, EMIT_DATA_PROCESS_INS(MOV_DP, 0, dst, SLJIT_UNUSED, (16 << 7) | reg_map[src2])));
			return push_inst(compiler, EMIT_DATA_PROCESS_INS(MOV_DP, 0, dst, SLJIT_UNUSED, (16 << 7) | (op == SLJIT_MOV_UH ? 0x20 : 0x40) | reg_map[dst]));
#else
			return push_inst(compiler, (op == SLJIT_MOV_UH ? UXTH : SXTH) | RD(dst) | RM(src2));
#endif
		}
		else if (dst != src2) {
			SLJIT_ASSERT(src2 & SRC2_IMM);
			if (flags & INV_IMM)
				EMIT_FULL_DATA_PROCESS_INS_AND_RETURN(MVN_DP, dst, SLJIT_UNUSED, src2);
			EMIT_FULL_DATA_PROCESS_INS_AND_RETURN(MOV_DP, dst, SLJIT_UNUSED, src2);
		}
		return SLJIT_SUCCESS;

	case SLJIT_NOT:
		if (src2 & SRC2_IMM) {
			if (flags & INV_IMM)
				EMIT_FULL_DATA_PROCESS_INS_AND_RETURN(MOV_DP, dst, SLJIT_UNUSED, src2);
			EMIT_FULL_DATA_PROCESS_INS_AND_RETURN(MVN_DP, dst, SLJIT_UNUSED, src2);
		}
		EMIT_FULL_DATA_PROCESS_INS_AND_RETURN(MVN_DP, dst, SLJIT_UNUSED, RM(src2));

	case SLJIT_CLZ:
		SLJIT_ASSERT(!(flags & INV_IMM));
		SLJIT_ASSERT(!(src2 & SRC2_IMM));
		FAIL_IF(push_inst(compiler, CLZ | RD(dst) | RM(src2)));
		if (flags & SET_FLAGS)
			EMIT_FULL_DATA_PROCESS_INS_AND_RETURN(CMP_DP, SLJIT_UNUSED, dst, SRC2_IMM);
		return SLJIT_SUCCESS;

	case SLJIT_ADD:
		SLJIT_ASSERT(!(flags & INV_IMM));
		EMIT_DATA_PROCESS_INS_AND_RETURN(ADD_DP);

	case SLJIT_ADDC:
		SLJIT_ASSERT(!(flags & INV_IMM));
		EMIT_DATA_PROCESS_INS_AND_RETURN(ADC_DP);

	case SLJIT_SUB:
		SLJIT_ASSERT(!(flags & INV_IMM));
		if (!(flags & ARGS_SWAPPED))
			EMIT_DATA_PROCESS_INS_AND_RETURN(SUB_DP);
		EMIT_DATA_PROCESS_INS_AND_RETURN(RSB_DP);

	case SLJIT_SUBC:
		SLJIT_ASSERT(!(flags & INV_IMM));
		if (!(flags & ARGS_SWAPPED))
			EMIT_DATA_PROCESS_INS_AND_RETURN(SBC_DP);
		EMIT_DATA_PROCESS_INS_AND_RETURN(RSC_DP);

	case SLJIT_MUL:
		SLJIT_ASSERT(!(flags & INV_IMM));
		SLJIT_ASSERT(!(src2 & SRC2_IMM));
		if (SLJIT_UNLIKELY(op & SLJIT_SET_O))
			mul_inst = SMULL | (reg_map[TMP_REG3] << 16) | (reg_map[dst] << 12);
		else
			mul_inst = MUL | (reg_map[dst] << 16);

		if (dst != src2)
			FAIL_IF(push_inst(compiler, mul_inst | (reg_map[src1] << 8) | reg_map[src2]));
		else if (dst != src1)
			FAIL_IF(push_inst(compiler, mul_inst | (reg_map[src2] << 8) | reg_map[src1]));
		else {
			/* Rm and Rd must not be the same register. */
			SLJIT_ASSERT(dst != TMP_REG1);
			FAIL_IF(push_inst(compiler, EMIT_DATA_PROCESS_INS(MOV_DP, 0, TMP_REG1, SLJIT_UNUSED, reg_map[src2])));
			FAIL_IF(push_inst(compiler, mul_inst | (reg_map[src2] << 8) | reg_map[TMP_REG1]));
		}

		if (!(op & SLJIT_SET_O))
			return SLJIT_SUCCESS;

		/* We need to use TMP_REG3. */
		compiler->cache_arg = 0;
		compiler->cache_argw = 0;
		/* cmp TMP_REG2, dst asr #31. */
		return push_inst(compiler, EMIT_DATA_PROCESS_INS(CMP_DP, SET_FLAGS, SLJIT_UNUSED, TMP_REG3, RM(dst) | 0xfc0));

	case SLJIT_AND:
		if (!(flags & INV_IMM))
			EMIT_DATA_PROCESS_INS_AND_RETURN(AND_DP);
		EMIT_DATA_PROCESS_INS_AND_RETURN(BIC_DP);

	case SLJIT_OR:
		SLJIT_ASSERT(!(flags & INV_IMM));
		EMIT_DATA_PROCESS_INS_AND_RETURN(ORR_DP);

	case SLJIT_XOR:
		SLJIT_ASSERT(!(flags & INV_IMM));
		EMIT_DATA_PROCESS_INS_AND_RETURN(EOR_DP);

	case SLJIT_SHL:
		EMIT_SHIFT_INS_AND_RETURN(0);

	case SLJIT_LSHR:
		EMIT_SHIFT_INS_AND_RETURN(1);

	case SLJIT_ASHR:
		EMIT_SHIFT_INS_AND_RETURN(2);
	}
	SLJIT_ASSERT_STOP();
	return SLJIT_SUCCESS;
}

#undef EMIT_DATA_PROCESS_INS_AND_RETURN
#undef EMIT_FULL_DATA_PROCESS_INS_AND_RETURN
#undef EMIT_SHIFT_INS_AND_RETURN

/* Tests whether the immediate can be stored in the 12 bit imm field.
   Returns with 0 if not possible. */
static sljit_uw get_imm(sljit_uw imm)
{
	sljit_si rol;

	if (imm <= 0xff)
		return SRC2_IMM | imm;

	if (!(imm & 0xff000000)) {
		imm <<= 8;
		rol = 8;
	}
	else {
		imm = (imm << 24) | (imm >> 8);
		rol = 0;
	}

	if (!(imm & 0xff000000)) {
		imm <<= 8;
		rol += 4;
	}

	if (!(imm & 0xf0000000)) {
		imm <<= 4;
		rol += 2;
	}

	if (!(imm & 0xc0000000)) {
		imm <<= 2;
		rol += 1;
	}

	if (!(imm & 0x00ffffff))
		return SRC2_IMM | (imm >> 24) | (rol << 8);
	else
		return 0;
}

#if (defined SLJIT_CONFIG_ARM_V5 && SLJIT_CONFIG_ARM_V5)
static sljit_si generate_int(struct sljit_compiler *compiler, sljit_si reg, sljit_uw imm, sljit_si positive)
{
	sljit_uw mask;
	sljit_uw imm1;
	sljit_uw imm2;
	sljit_si rol;

	/* Step1: Search a zero byte (8 continous zero bit). */
	mask = 0xff000000;
	rol = 8;
	while(1) {
		if (!(imm & mask)) {
			/* Rol imm by rol. */
			imm = (imm << rol) | (imm >> (32 - rol));
			/* Calculate arm rol. */
			rol = 4 + (rol >> 1);
			break;
		}
		rol += 2;
		mask >>= 2;
		if (mask & 0x3) {
			/* rol by 8. */
			imm = (imm << 8) | (imm >> 24);
			mask = 0xff00;
			rol = 24;
			while (1) {
				if (!(imm & mask)) {
					/* Rol imm by rol. */
					imm = (imm << rol) | (imm >> (32 - rol));
					/* Calculate arm rol. */
					rol = (rol >> 1) - 8;
					break;
				}
				rol += 2;
				mask >>= 2;
				if (mask & 0x3)
					return 0;
			}
			break;
		}
	}

	/* The low 8 bit must be zero. */
	SLJIT_ASSERT(!(imm & 0xff));

	if (!(imm & 0xff000000)) {
		imm1 = SRC2_IMM | ((imm >> 16) & 0xff) | (((rol + 4) & 0xf) << 8);
		imm2 = SRC2_IMM | ((imm >> 8) & 0xff) | (((rol + 8) & 0xf) << 8);
	}
	else if (imm & 0xc0000000) {
		imm1 = SRC2_IMM | ((imm >> 24) & 0xff) | ((rol & 0xf) << 8);
		imm <<= 8;
		rol += 4;

		if (!(imm & 0xff000000)) {
			imm <<= 8;
			rol += 4;
		}

		if (!(imm & 0xf0000000)) {
			imm <<= 4;
			rol += 2;
		}

		if (!(imm & 0xc0000000)) {
			imm <<= 2;
			rol += 1;
		}

		if (!(imm & 0x00ffffff))
			imm2 = SRC2_IMM | (imm >> 24) | ((rol & 0xf) << 8);
		else
			return 0;
	}
	else {
		if (!(imm & 0xf0000000)) {
			imm <<= 4;
			rol += 2;
		}

		if (!(imm & 0xc0000000)) {
			imm <<= 2;
			rol += 1;
		}

		imm1 = SRC2_IMM | ((imm >> 24) & 0xff) | ((rol & 0xf) << 8);
		imm <<= 8;
		rol += 4;

		if (!(imm & 0xf0000000)) {
			imm <<= 4;
			rol += 2;
		}

		if (!(imm & 0xc0000000)) {
			imm <<= 2;
			rol += 1;
		}

		if (!(imm & 0x00ffffff))
			imm2 = SRC2_IMM | (imm >> 24) | ((rol & 0xf) << 8);
		else
			return 0;
	}

	FAIL_IF(push_inst(compiler, EMIT_DATA_PROCESS_INS(positive ? MOV_DP : MVN_DP, 0, reg, SLJIT_UNUSED, imm1)));
	FAIL_IF(push_inst(compiler, EMIT_DATA_PROCESS_INS(positive ? ORR_DP : BIC_DP, 0, reg, reg, imm2)));
	return 1;
}
#endif

static sljit_si load_immediate(struct sljit_compiler *compiler, sljit_si reg, sljit_uw imm)
{
	sljit_uw tmp;

#if (defined SLJIT_CONFIG_ARM_V7 && SLJIT_CONFIG_ARM_V7)
	if (!(imm & ~0xffff))
		return push_inst(compiler, MOVW | RD(reg) | ((imm << 4) & 0xf0000) | (imm & 0xfff));
#endif

	/* Create imm by 1 inst. */
	tmp = get_imm(imm);
	if (tmp)
		return push_inst(compiler, EMIT_DATA_PROCESS_INS(MOV_DP, 0, reg, SLJIT_UNUSED, tmp));

	tmp = get_imm(~imm);
	if (tmp)
		return push_inst(compiler, EMIT_DATA_PROCESS_INS(MVN_DP, 0, reg, SLJIT_UNUSED, tmp));

#if (defined SLJIT_CONFIG_ARM_V5 && SLJIT_CONFIG_ARM_V5)
	/* Create imm by 2 inst. */
	FAIL_IF(generate_int(compiler, reg, imm, 1));
	FAIL_IF(generate_int(compiler, reg, ~imm, 0));

	/* Load integer. */
	return push_inst_with_literal(compiler, EMIT_DATA_TRANSFER(WORD_DATA | LOAD_DATA, 1, 0, reg, TMP_PC, 0), imm);
#else
	return emit_imm(compiler, reg, imm);
#endif
}

/* Helper function. Dst should be reg + value, using at most 1 instruction, flags does not set. */
static sljit_si emit_set_delta(struct sljit_compiler *compiler, sljit_si dst, sljit_si reg, sljit_sw value)
{
	if (value >= 0) {
		value = get_imm(value);
		if (value)
			return push_inst(compiler, EMIT_DATA_PROCESS_INS(ADD_DP, 0, dst, reg, value));
	}
	else {
		value = get_imm(-value);
		if (value)
			return push_inst(compiler, EMIT_DATA_PROCESS_INS(SUB_DP, 0, dst, reg, value));
	}
	return SLJIT_ERR_UNSUPPORTED;
}

/* Can perform an operation using at most 1 instruction. */
static sljit_si getput_arg_fast(struct sljit_compiler *compiler, sljit_si inp_flags, sljit_si reg, sljit_si arg, sljit_sw argw)
{
	sljit_uw imm;

	if (arg & SLJIT_IMM) {
		imm = get_imm(argw);
		if (imm) {
			if (inp_flags & ARG_TEST)
				return 1;
			FAIL_IF(push_inst(compiler, EMIT_DATA_PROCESS_INS(MOV_DP, 0, reg, SLJIT_UNUSED, imm)));
			return -1;
		}
		imm = get_imm(~argw);
		if (imm) {
			if (inp_flags & ARG_TEST)
				return 1;
			FAIL_IF(push_inst(compiler, EMIT_DATA_PROCESS_INS(MVN_DP, 0, reg, SLJIT_UNUSED, imm)));
			return -1;
		}
		return 0;
	}

	SLJIT_ASSERT(arg & SLJIT_MEM);

	/* Fast loads/stores. */
	if (!(arg & REG_MASK))
		return 0;

	if (arg & OFFS_REG_MASK) {
		if ((argw & 0x3) != 0 && !IS_TYPE1_TRANSFER(inp_flags))
			return 0;

		if (inp_flags & ARG_TEST)
			return 1;
		FAIL_IF(push_inst(compiler, EMIT_DATA_TRANSFER(inp_flags, 1, inp_flags & WRITE_BACK, reg, arg & REG_MASK,
			RM(OFFS_REG(arg)) | (IS_TYPE1_TRANSFER(inp_flags) ? SRC2_IMM : 0) | ((argw & 0x3) << 7))));
		return -1;
	}

	if (IS_TYPE1_TRANSFER(inp_flags)) {
		if (argw >= 0 && argw <= 0xfff) {
			if (inp_flags & ARG_TEST)
				return 1;
			FAIL_IF(push_inst(compiler, EMIT_DATA_TRANSFER(inp_flags, 1, inp_flags & WRITE_BACK, reg, arg & REG_MASK, argw)));
			return -1;
		}
		if (argw < 0 && argw >= -0xfff) {
			if (inp_flags & ARG_TEST)
				return 1;
			FAIL_IF(push_inst(compiler, EMIT_DATA_TRANSFER(inp_flags, 0, inp_flags & WRITE_BACK, reg, arg & REG_MASK, -argw)));
			return -1;
		}
	}
	else {
		if (argw >= 0 && argw <= 0xff) {
			if (inp_flags & ARG_TEST)
				return 1;
			FAIL_IF(push_inst(compiler, EMIT_DATA_TRANSFER(inp_flags, 1, inp_flags & WRITE_BACK, reg, arg & REG_MASK, TYPE2_TRANSFER_IMM(argw))));
			return -1;
		}
		if (argw < 0 && argw >= -0xff) {
			if (inp_flags & ARG_TEST)
				return 1;
			argw = -argw;
			FAIL_IF(push_inst(compiler, EMIT_DATA_TRANSFER(inp_flags, 0, inp_flags & WRITE_BACK, reg, arg & REG_MASK, TYPE2_TRANSFER_IMM(argw))));
			return -1;
		}
	}

	return 0;
}

/* See getput_arg below.
   Note: can_cache is called only for binary operators. Those
   operators always uses word arguments without write back. */
static sljit_si can_cache(sljit_si arg, sljit_sw argw, sljit_si next_arg, sljit_sw next_argw)
{
	/* Immediate caching is not supported as it would be an operation on constant arguments. */
	if (arg & SLJIT_IMM)
		return 0;

	/* Always a simple operation. */
	if (arg & OFFS_REG_MASK)
		return 0;

	if (!(arg & REG_MASK)) {
		/* Immediate access. */
		if ((next_arg & SLJIT_MEM) && ((sljit_uw)argw - (sljit_uw)next_argw <= 0xfff || (sljit_uw)next_argw - (sljit_uw)argw <= 0xfff))
			return 1;
		return 0;
	}

	if (argw <= 0xfffff && argw >= -0xfffff)
		return 0;

	if (argw == next_argw && (next_arg & SLJIT_MEM))
		return 1;

	if (arg == next_arg && ((sljit_uw)argw - (sljit_uw)next_argw <= 0xfff || (sljit_uw)next_argw - (sljit_uw)argw <= 0xfff))
		return 1;

	return 0;
}

#define GETPUT_ARG_DATA_TRANSFER(add, wb, target, base, imm) \
	if (max_delta & 0xf00) \
		FAIL_IF(push_inst(compiler, EMIT_DATA_TRANSFER(inp_flags, add, wb, target, base, imm))); \
	else \
		FAIL_IF(push_inst(compiler, EMIT_DATA_TRANSFER(inp_flags, add, wb, target, base, TYPE2_TRANSFER_IMM(imm))));

#define TEST_WRITE_BACK() \
	if (inp_flags & WRITE_BACK) { \
		tmp_r = arg & REG_MASK; \
		if (reg == tmp_r) { \
			/* This can only happen for stores */ \
			/* since ldr reg, [reg, ...]! has no meaning */ \
			SLJIT_ASSERT(!(inp_flags & LOAD_DATA)); \
			FAIL_IF(push_inst(compiler, EMIT_DATA_PROCESS_INS(MOV_DP, 0, TMP_REG3, SLJIT_UNUSED, RM(reg)))); \
			reg = TMP_REG3; \
		} \
	}

/* Emit the necessary instructions. See can_cache above. */
static sljit_si getput_arg(struct sljit_compiler *compiler, sljit_si inp_flags, sljit_si reg, sljit_si arg, sljit_sw argw, sljit_si next_arg, sljit_sw next_argw)
{
	sljit_si tmp_r;
	sljit_sw max_delta;
	sljit_sw sign;
	sljit_uw imm;

	if (arg & SLJIT_IMM) {
		SLJIT_ASSERT(inp_flags & LOAD_DATA);
		return load_immediate(compiler, reg, argw);
	}

	SLJIT_ASSERT(arg & SLJIT_MEM);

	tmp_r = (inp_flags & LOAD_DATA) ? reg : TMP_REG3;
	max_delta = IS_TYPE1_TRANSFER(inp_flags) ? 0xfff : 0xff;

	if ((arg & REG_MASK) == SLJIT_UNUSED) {
		/* Write back is not used. */
		imm = (sljit_uw)(argw - compiler->cache_argw);
		if ((compiler->cache_arg & SLJIT_IMM) && (imm <= (sljit_uw)max_delta || imm >= (sljit_uw)-max_delta)) {
			if (imm <= (sljit_uw)max_delta) {
				sign = 1;
				argw = argw - compiler->cache_argw;
			}
			else {
				sign = 0;
				argw = compiler->cache_argw - argw;
			}

			GETPUT_ARG_DATA_TRANSFER(sign, 0, reg, TMP_REG3, argw);
			return SLJIT_SUCCESS;
		}

		/* With write back, we can create some sophisticated loads, but
		   it is hard to decide whether we should convert downward (0s) or upward (1s). */
		imm = (sljit_uw)(argw - next_argw);
		if ((next_arg & SLJIT_MEM) && (imm <= (sljit_uw)max_delta || imm >= (sljit_uw)-max_delta)) {
			SLJIT_ASSERT(inp_flags & LOAD_DATA);

			compiler->cache_arg = SLJIT_IMM;
			compiler->cache_argw = argw;
			tmp_r = TMP_REG3;
		}

		FAIL_IF(load_immediate(compiler, tmp_r, argw));
		GETPUT_ARG_DATA_TRANSFER(1, 0, reg, tmp_r, 0);
		return SLJIT_SUCCESS;
	}

	if (arg & OFFS_REG_MASK) {
		SLJIT_ASSERT((argw & 0x3) && !(max_delta & 0xf00));
		if (inp_flags & WRITE_BACK)
			tmp_r = arg & REG_MASK;
		FAIL_IF(push_inst(compiler, EMIT_DATA_PROCESS_INS(ADD_DP, 0, tmp_r, arg & REG_MASK, RM(OFFS_REG(arg)) | ((argw & 0x3) << 7))));
		return push_inst(compiler, EMIT_DATA_TRANSFER(inp_flags, 1, 0, reg, tmp_r, TYPE2_TRANSFER_IMM(0)));
	}

	imm = (sljit_uw)(argw - compiler->cache_argw);
	if (compiler->cache_arg == arg && imm <= (sljit_uw)max_delta) {
		SLJIT_ASSERT(!(inp_flags & WRITE_BACK));
		GETPUT_ARG_DATA_TRANSFER(1, 0, reg, TMP_REG3, imm);
		return SLJIT_SUCCESS;
	}
	if (compiler->cache_arg == arg && imm >= (sljit_uw)-max_delta) {
		SLJIT_ASSERT(!(inp_flags & WRITE_BACK));
		imm = (sljit_uw)-(sljit_sw)imm;
		GETPUT_ARG_DATA_TRANSFER(0, 0, reg, TMP_REG3, imm);
		return SLJIT_SUCCESS;
	}

	imm = get_imm(argw & ~max_delta);
	if (imm) {
		TEST_WRITE_BACK();
		FAIL_IF(push_inst(compiler, EMIT_DATA_PROCESS_INS(ADD_DP, 0, tmp_r, arg & REG_MASK, imm)));
		GETPUT_ARG_DATA_TRANSFER(1, inp_flags & WRITE_BACK, reg, tmp_r, argw & max_delta);
		return SLJIT_SUCCESS;
	}

	imm = get_imm(-argw & ~max_delta);
	if (imm) {
		argw = -argw;
		TEST_WRITE_BACK();
		FAIL_IF(push_inst(compiler, EMIT_DATA_PROCESS_INS(SUB_DP, 0, tmp_r, arg & REG_MASK, imm)));
		GETPUT_ARG_DATA_TRANSFER(0, inp_flags & WRITE_BACK, reg, tmp_r, argw & max_delta);
		return SLJIT_SUCCESS;
	}

	if ((compiler->cache_arg & SLJIT_IMM) && compiler->cache_argw == argw) {
		TEST_WRITE_BACK();
		return push_inst(compiler, EMIT_DATA_TRANSFER(inp_flags, 1, inp_flags & WRITE_BACK, reg, arg & REG_MASK, RM(TMP_REG3) | (max_delta & 0xf00 ? SRC2_IMM : 0)));
	}

	if (argw == next_argw && (next_arg & SLJIT_MEM)) {
		SLJIT_ASSERT(inp_flags & LOAD_DATA);
		FAIL_IF(load_immediate(compiler, TMP_REG3, argw));

		compiler->cache_arg = SLJIT_IMM;
		compiler->cache_argw = argw;

		TEST_WRITE_BACK();
		return push_inst(compiler, EMIT_DATA_TRANSFER(inp_flags, 1, inp_flags & WRITE_BACK, reg, arg & REG_MASK, RM(TMP_REG3) | (max_delta & 0xf00 ? SRC2_IMM : 0)));
	}

	imm = (sljit_uw)(argw - next_argw);
	if (arg == next_arg && !(inp_flags & WRITE_BACK) && (imm <= (sljit_uw)max_delta || imm >= (sljit_uw)-max_delta)) {
		SLJIT_ASSERT(inp_flags & LOAD_DATA);
		FAIL_IF(load_immediate(compiler, TMP_REG3, argw));
		FAIL_IF(push_inst(compiler, EMIT_DATA_PROCESS_INS(ADD_DP, 0, TMP_REG3, TMP_REG3, reg_map[arg & REG_MASK])));

		compiler->cache_arg = arg;
		compiler->cache_argw = argw;

		GETPUT_ARG_DATA_TRANSFER(1, 0, reg, TMP_REG3, 0);
		return SLJIT_SUCCESS;
	}

	if ((arg & REG_MASK) == tmp_r) {
		compiler->cache_arg = SLJIT_IMM;
		compiler->cache_argw = argw;
		tmp_r = TMP_REG3;
	}

	FAIL_IF(load_immediate(compiler, tmp_r, argw));
	return push_inst(compiler, EMIT_DATA_TRANSFER(inp_flags, 1, inp_flags & WRITE_BACK, reg, arg & REG_MASK, reg_map[tmp_r] | (max_delta & 0xf00 ? SRC2_IMM : 0)));
}

static SLJIT_INLINE sljit_si emit_op_mem(struct sljit_compiler *compiler, sljit_si flags, sljit_si reg, sljit_si arg, sljit_sw argw)
{
	if (getput_arg_fast(compiler, flags, reg, arg, argw))
		return compiler->error;
	compiler->cache_arg = 0;
	compiler->cache_argw = 0;
	return getput_arg(compiler, flags, reg, arg, argw, 0, 0);
}

static SLJIT_INLINE sljit_si emit_op_mem2(struct sljit_compiler *compiler, sljit_si flags, sljit_si reg, sljit_si arg1, sljit_sw arg1w, sljit_si arg2, sljit_sw arg2w)
{
	if (getput_arg_fast(compiler, flags, reg, arg1, arg1w))
		return compiler->error;
	return getput_arg(compiler, flags, reg, arg1, arg1w, arg2, arg2w);
}

static sljit_si emit_op(struct sljit_compiler *compiler, sljit_si op, sljit_si inp_flags,
	sljit_si dst, sljit_sw dstw,
	sljit_si src1, sljit_sw src1w,
	sljit_si src2, sljit_sw src2w)
{
	/* arg1 goes to TMP_REG1 or src reg
	   arg2 goes to TMP_REG2, imm or src reg
	   TMP_REG3 can be used for caching
	   result goes to TMP_REG2, so put result can use TMP_REG1 and TMP_REG3. */

	/* We prefers register and simple consts. */
	sljit_si dst_r;
	sljit_si src1_r;
	sljit_si src2_r = 0;
	sljit_si sugg_src2_r = TMP_REG2;
	sljit_si flags = GET_FLAGS(op) ? SET_FLAGS : 0;

	compiler->cache_arg = 0;
	compiler->cache_argw = 0;

	/* Destination check. */
	if (SLJIT_UNLIKELY(dst == SLJIT_UNUSED)) {
		if (op >= SLJIT_MOV && op <= SLJIT_MOVU_SI && !(src2 & SLJIT_MEM))
			return SLJIT_SUCCESS;
		dst_r = TMP_REG2;
	}
	else if (FAST_IS_REG(dst)) {
		dst_r = dst;
		flags |= REG_DEST;
		if (op >= SLJIT_MOV && op <= SLJIT_MOVU_SI)
			sugg_src2_r = dst_r;
	}
	else {
		SLJIT_ASSERT(dst & SLJIT_MEM);
		if (getput_arg_fast(compiler, inp_flags | ARG_TEST, TMP_REG2, dst, dstw)) {
			flags |= FAST_DEST;
			dst_r = TMP_REG2;
		}
		else {
			flags |= SLOW_DEST;
			dst_r = 0;
		}
	}

	/* Source 1. */
	if (FAST_IS_REG(src1))
		src1_r = src1;
	else if (FAST_IS_REG(src2)) {
		flags |= ARGS_SWAPPED;
		src1_r = src2;
		src2 = src1;
		src2w = src1w;
	}
	else do { /* do { } while(0) is used because of breaks. */
		src1_r = 0;
		if ((inp_flags & ALLOW_ANY_IMM) && (src1 & SLJIT_IMM)) {
			/* The second check will generate a hit. */
			src2_r = get_imm(src1w);
			if (src2_r) {
				flags |= ARGS_SWAPPED;
				src1 = src2;
				src1w = src2w;
				break;
			}
			if (inp_flags & ALLOW_INV_IMM) {
				src2_r = get_imm(~src1w);
				if (src2_r) {
					flags |= ARGS_SWAPPED | INV_IMM;
					src1 = src2;
					src1w = src2w;
					break;
				}
			}
			if (GET_OPCODE(op) == SLJIT_ADD) {
				src2_r = get_imm(-src1w);
				if (src2_r) {
					/* Note: ARGS_SWAPPED is intentionally not applied! */
					src1 = src2;
					src1w = src2w;
					op = SLJIT_SUB | GET_ALL_FLAGS(op);
					break;
				}
			}
		}

		if (getput_arg_fast(compiler, inp_flags | LOAD_DATA, TMP_REG1, src1, src1w)) {
			FAIL_IF(compiler->error);
			src1_r = TMP_REG1;
		}
	} while (0);

	/* Source 2. */
	if (src2_r == 0) {
		if (FAST_IS_REG(src2)) {
			src2_r = src2;
			flags |= REG_SOURCE;
			if (!(flags & REG_DEST) && op >= SLJIT_MOV && op <= SLJIT_MOVU_SI)
				dst_r = src2_r;
		}
		else do { /* do { } while(0) is used because of breaks. */
			if ((inp_flags & ALLOW_ANY_IMM) && (src2 & SLJIT_IMM)) {
				src2_r = get_imm(src2w);
				if (src2_r)
					break;
				if (inp_flags & ALLOW_INV_IMM) {
					src2_r = get_imm(~src2w);
					if (src2_r) {
						flags |= INV_IMM;
						break;
					}
				}
				if (GET_OPCODE(op) == SLJIT_ADD) {
					src2_r = get_imm(-src2w);
					if (src2_r) {
						op = SLJIT_SUB | GET_ALL_FLAGS(op);
						flags &= ~ARGS_SWAPPED;
						break;
					}
				}
				if (GET_OPCODE(op) == SLJIT_SUB && !(flags & ARGS_SWAPPED)) {
					src2_r = get_imm(-src2w);
					if (src2_r) {
						op = SLJIT_ADD | GET_ALL_FLAGS(op);
						flags &= ~ARGS_SWAPPED;
						break;
					}
				}
			}

			/* src2_r is 0. */
			if (getput_arg_fast(compiler, inp_flags | LOAD_DATA, sugg_src2_r, src2, src2w)) {
				FAIL_IF(compiler->error);
				src2_r = sugg_src2_r;
			}
		} while (0);
	}

	/* src1_r, src2_r and dst_r can be zero (=unprocessed) or non-zero.
	   If they are zero, they must not be registers. */
	if (src1_r == 0 && src2_r == 0 && dst_r == 0) {
		if (!can_cache(src1, src1w, src2, src2w) && can_cache(src1, src1w, dst, dstw)) {
			SLJIT_ASSERT(!(flags & ARGS_SWAPPED));
			flags |= ARGS_SWAPPED;
			FAIL_IF(getput_arg(compiler, inp_flags | LOAD_DATA, TMP_REG1, src2, src2w, src1, src1w));
			FAIL_IF(getput_arg(compiler, inp_flags | LOAD_DATA, TMP_REG2, src1, src1w, dst, dstw));
		}
		else {
			FAIL_IF(getput_arg(compiler, inp_flags | LOAD_DATA, TMP_REG1, src1, src1w, src2, src2w));
			FAIL_IF(getput_arg(compiler, inp_flags | LOAD_DATA, TMP_REG2, src2, src2w, dst, dstw));
		}
		src1_r = TMP_REG1;
		src2_r = TMP_REG2;
	}
	else if (src1_r == 0 && src2_r == 0) {
		FAIL_IF(getput_arg(compiler, inp_flags | LOAD_DATA, TMP_REG1, src1, src1w, src2, src2w));
		src1_r = TMP_REG1;
	}
	else if (src1_r == 0 && dst_r == 0) {
		FAIL_IF(getput_arg(compiler, inp_flags | LOAD_DATA, TMP_REG1, src1, src1w, dst, dstw));
		src1_r = TMP_REG1;
	}
	else if (src2_r == 0 && dst_r == 0) {
		FAIL_IF(getput_arg(compiler, inp_flags | LOAD_DATA, sugg_src2_r, src2, src2w, dst, dstw));
		src2_r = sugg_src2_r;
	}

	if (dst_r == 0)
		dst_r = TMP_REG2;

	if (src1_r == 0) {
		FAIL_IF(getput_arg(compiler, inp_flags | LOAD_DATA, TMP_REG1, src1, src1w, 0, 0));
		src1_r = TMP_REG1;
	}

	if (src2_r == 0) {
		FAIL_IF(getput_arg(compiler, inp_flags | LOAD_DATA, sugg_src2_r, src2, src2w, 0, 0));
		src2_r = sugg_src2_r;
	}

	FAIL_IF(emit_single_op(compiler, op, flags, dst_r, src1_r, src2_r));

	if (flags & (FAST_DEST | SLOW_DEST)) {
		if (flags & FAST_DEST)
			FAIL_IF(getput_arg_fast(compiler, inp_flags, dst_r, dst, dstw));
		else
			FAIL_IF(getput_arg(compiler, inp_flags, dst_r, dst, dstw, 0, 0));
	}
	return SLJIT_SUCCESS;
}

#ifdef __cplusplus
extern "C" {
#endif

#if defined(__GNUC__)
extern unsigned int __aeabi_uidivmod(unsigned int numerator, unsigned int denominator);
extern int __aeabi_idivmod(int numerator, int denominator);
#else
#error "Software divmod functions are needed"
#endif

#ifdef __cplusplus
}
#endif

SLJIT_API_FUNC_ATTRIBUTE sljit_si sljit_emit_op0(struct sljit_compiler *compiler, sljit_si op)
{
	CHECK_ERROR();
	CHECK(check_sljit_emit_op0(compiler, op));

	op = GET_OPCODE(op);
	switch (op) {
	case SLJIT_BREAKPOINT:
		FAIL_IF(push_inst(compiler, BKPT));
		break;
	case SLJIT_NOP:
		FAIL_IF(push_inst(compiler, NOP));
		break;
	case SLJIT_LUMUL:
	case SLJIT_LSMUL:
#if (defined SLJIT_CONFIG_ARM_V7 && SLJIT_CONFIG_ARM_V7)
		return push_inst(compiler, (op == SLJIT_LUMUL ? UMULL : SMULL)
			| (reg_map[SLJIT_R1] << 16)
			| (reg_map[SLJIT_R0] << 12)
			| (reg_map[SLJIT_R0] << 8)
			| reg_map[SLJIT_R1]);
#else
		FAIL_IF(push_inst(compiler, EMIT_DATA_PROCESS_INS(MOV_DP, 0, TMP_REG1, SLJIT_UNUSED, RM(SLJIT_R1))));
		return push_inst(compiler, (op == SLJIT_LUMUL ? UMULL : SMULL)
			| (reg_map[SLJIT_R1] << 16)
			| (reg_map[SLJIT_R0] << 12)
			| (reg_map[SLJIT_R0] << 8)
			| reg_map[TMP_REG1]);
#endif
	case SLJIT_UDIVMOD:
	case SLJIT_SDIVMOD:
	case SLJIT_UDIVI:
	case SLJIT_SDIVI:
		SLJIT_COMPILE_ASSERT((SLJIT_UDIVMOD & 0x2) == 0 && SLJIT_UDIVI - 0x2 == SLJIT_UDIVMOD, bad_div_opcode_assignments);
		SLJIT_COMPILE_ASSERT(reg_map[2] == 1 && reg_map[3] == 2, bad_register_mapping);

		if ((op >= SLJIT_UDIVI) && (compiler->scratches >= 3)) {
			FAIL_IF(push_inst(compiler, 0xe52d2008 /* str r2, [sp, #-8]! */));
			FAIL_IF(push_inst(compiler, 0xe58d1004 /* str r1, [sp, #4] */));
		}
		else if ((op >= SLJIT_UDIVI) || (compiler->scratches >= 3))
			FAIL_IF(push_inst(compiler, 0xe52d0008 | (op >= SLJIT_UDIVI ? 0x1000 : 0x2000) /* str r1/r2, [sp, #-8]! */));

#if defined(__GNUC__)
		FAIL_IF(sljit_emit_ijump(compiler, SLJIT_FAST_CALL, SLJIT_IMM,
			((op | 0x2) == SLJIT_UDIVI ? SLJIT_FUNC_OFFSET(__aeabi_uidivmod) : SLJIT_FUNC_OFFSET(__aeabi_idivmod))));
#else
#error "Software divmod functions are needed"
#endif

		if ((op >= SLJIT_UDIVI) && (compiler->scratches >= 3)) {
			FAIL_IF(push_inst(compiler, 0xe59d1004 /* ldr r1, [sp, #4] */));
			FAIL_IF(push_inst(compiler, 0xe49d2008 /* ldr r2, [sp], #8 */));
		}
		else if ((op >= SLJIT_UDIVI) || (compiler->scratches >= 3))
			return push_inst(compiler, 0xe49d0008 | (op >= SLJIT_UDIVI ? 0x1000 : 0x2000) /* ldr r1/r2, [sp], #8 */);
		return SLJIT_SUCCESS;
	}

	return SLJIT_SUCCESS;
}

SLJIT_API_FUNC_ATTRIBUTE sljit_si sljit_emit_op1(struct sljit_compiler *compiler, sljit_si op,
	sljit_si dst, sljit_sw dstw,
	sljit_si src, sljit_sw srcw)
{
	CHECK_ERROR();
	CHECK(check_sljit_emit_op1(compiler, op, dst, dstw, src, srcw));
	ADJUST_LOCAL_OFFSET(dst, dstw);
	ADJUST_LOCAL_OFFSET(src, srcw);

	switch (GET_OPCODE(op)) {
	case SLJIT_MOV:
	case SLJIT_MOV_UI:
	case SLJIT_MOV_SI:
	case SLJIT_MOV_P:
		return emit_op(compiler, SLJIT_MOV, ALLOW_ANY_IMM, dst, dstw, TMP_REG1, 0, src, srcw);

	case SLJIT_MOV_UB:
		return emit_op(compiler, SLJIT_MOV_UB, ALLOW_ANY_IMM | BYTE_DATA, dst, dstw, TMP_REG1, 0, src, (src & SLJIT_IMM) ? (sljit_ub)srcw : srcw);

	case SLJIT_MOV_SB:
		return emit_op(compiler, SLJIT_MOV_SB, ALLOW_ANY_IMM | SIGNED_DATA | BYTE_DATA, dst, dstw, TMP_REG1, 0, src, (src & SLJIT_IMM) ? (sljit_sb)srcw : srcw);

	case SLJIT_MOV_UH:
		return emit_op(compiler, SLJIT_MOV_UH, ALLOW_ANY_IMM | HALF_DATA, dst, dstw, TMP_REG1, 0, src, (src & SLJIT_IMM) ? (sljit_uh)srcw : srcw);

	case SLJIT_MOV_SH:
		return emit_op(compiler, SLJIT_MOV_SH, ALLOW_ANY_IMM | SIGNED_DATA | HALF_DATA, dst, dstw, TMP_REG1, 0, src, (src & SLJIT_IMM) ? (sljit_sh)srcw : srcw);

	case SLJIT_MOVU:
	case SLJIT_MOVU_UI:
	case SLJIT_MOVU_SI:
	case SLJIT_MOVU_P:
		return emit_op(compiler, SLJIT_MOV, ALLOW_ANY_IMM | WRITE_BACK, dst, dstw, TMP_REG1, 0, src, srcw);

	case SLJIT_MOVU_UB:
		return emit_op(compiler, SLJIT_MOV_UB, ALLOW_ANY_IMM | BYTE_DATA | WRITE_BACK, dst, dstw, TMP_REG1, 0, src, (src & SLJIT_IMM) ? (sljit_ub)srcw : srcw);

	case SLJIT_MOVU_SB:
		return emit_op(compiler, SLJIT_MOV_SB, ALLOW_ANY_IMM | SIGNED_DATA | BYTE_DATA | WRITE_BACK, dst, dstw, TMP_REG1, 0, src, (src & SLJIT_IMM) ? (sljit_sb)srcw : srcw);

	case SLJIT_MOVU_UH:
		return emit_op(compiler, SLJIT_MOV_UH, ALLOW_ANY_IMM | HALF_DATA | WRITE_BACK, dst, dstw, TMP_REG1, 0, src, (src & SLJIT_IMM) ? (sljit_uh)srcw : srcw);

	case SLJIT_MOVU_SH:
		return emit_op(compiler, SLJIT_MOV_SH, ALLOW_ANY_IMM | SIGNED_DATA | HALF_DATA | WRITE_BACK, dst, dstw, TMP_REG1, 0, src, (src & SLJIT_IMM) ? (sljit_sh)srcw : srcw);

	case SLJIT_NOT:
		return emit_op(compiler, op, ALLOW_ANY_IMM, dst, dstw, TMP_REG1, 0, src, srcw);

	case SLJIT_NEG:
#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE) \
			|| (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS)
		compiler->skip_checks = 1;
#endif
		return sljit_emit_op2(compiler, SLJIT_SUB | GET_ALL_FLAGS(op), dst, dstw, SLJIT_IMM, 0, src, srcw);

	case SLJIT_CLZ:
		return emit_op(compiler, op, 0, dst, dstw, TMP_REG1, 0, src, srcw);
	}

	return SLJIT_SUCCESS;
}

SLJIT_API_FUNC_ATTRIBUTE sljit_si sljit_emit_op2(struct sljit_compiler *compiler, sljit_si op,
	sljit_si dst, sljit_sw dstw,
	sljit_si src1, sljit_sw src1w,
	sljit_si src2, sljit_sw src2w)
{
	CHECK_ERROR();
	CHECK(check_sljit_emit_op2(compiler, op, dst, dstw, src1, src1w, src2, src2w));
	ADJUST_LOCAL_OFFSET(dst, dstw);
	ADJUST_LOCAL_OFFSET(src1, src1w);
	ADJUST_LOCAL_OFFSET(src2, src2w);

	switch (GET_OPCODE(op)) {
	case SLJIT_ADD:
	case SLJIT_ADDC:
	case SLJIT_SUB:
	case SLJIT_SUBC:
	case SLJIT_OR:
	case SLJIT_XOR:
		return emit_op(compiler, op, ALLOW_IMM, dst, dstw, src1, src1w, src2, src2w);

	case SLJIT_MUL:
		return emit_op(compiler, op, 0, dst, dstw, src1, src1w, src2, src2w);

	case SLJIT_AND:
		return emit_op(compiler, op, ALLOW_ANY_IMM, dst, dstw, src1, src1w, src2, src2w);

	case SLJIT_SHL:
	case SLJIT_LSHR:
	case SLJIT_ASHR:
		if (src2 & SLJIT_IMM) {
			compiler->shift_imm = src2w & 0x1f;
			return emit_op(compiler, op, 0, dst, dstw, TMP_REG1, 0, src1, src1w);
		}
		else {
			compiler->shift_imm = 0x20;
			return emit_op(compiler, op, 0, dst, dstw, src1, src1w, src2, src2w);
		}
	}

	return SLJIT_SUCCESS;
}

SLJIT_API_FUNC_ATTRIBUTE sljit_si sljit_get_register_index(sljit_si reg)
{
	CHECK_REG_INDEX(check_sljit_get_register_index(reg));
	return reg_map[reg];
}

SLJIT_API_FUNC_ATTRIBUTE sljit_si sljit_get_float_register_index(sljit_si reg)
{
	CHECK_REG_INDEX(check_sljit_get_float_register_index(reg));
	return reg << 1;
}

SLJIT_API_FUNC_ATTRIBUTE sljit_si sljit_emit_op_custom(struct sljit_compiler *compiler,
	void *instruction, sljit_si size)
{
	CHECK_ERROR();
	CHECK(check_sljit_emit_op_custom(compiler, instruction, size));

	return push_inst(compiler, *(sljit_uw*)instruction);
}

/* --------------------------------------------------------------------- */
/*  Floating point operators                                             */
/* --------------------------------------------------------------------- */

#if (defined SLJIT_CONFIG_ARM_V5 && SLJIT_CONFIG_ARM_V5)

/* 0 - no fpu
   1 - vfp */
static sljit_si arm_fpu_type = -1;

static void init_compiler(void)
{
	if (arm_fpu_type != -1)
		return;

	/* TODO: Only the OS can help to determine the correct fpu type. */
	arm_fpu_type = 1;
}

SLJIT_API_FUNC_ATTRIBUTE sljit_si sljit_is_fpu_available(void)
{
#ifdef SLJIT_IS_FPU_AVAILABLE
	return SLJIT_IS_FPU_AVAILABLE;
#else
	if (arm_fpu_type == -1)
		init_compiler();
	return arm_fpu_type;
#endif
}

#else

#define arm_fpu_type 1

SLJIT_API_FUNC_ATTRIBUTE sljit_si sljit_is_fpu_available(void)
{
	/* Always available. */
	return 1;
}

#endif

#define FPU_LOAD (1 << 20)
#define EMIT_FPU_DATA_TRANSFER(inst, add, base, freg, offs) \
	((inst) | ((add) << 23) | (reg_map[base] << 16) | (freg << 12) | (offs))
#define EMIT_FPU_OPERATION(opcode, mode, dst, src1, src2) \
	((opcode) | (mode) | ((dst) << 12) | (src1) | ((src2) << 16))

static sljit_si emit_fop_mem(struct sljit_compiler *compiler, sljit_si flags, sljit_si reg, sljit_si arg, sljit_sw argw)
{
	sljit_sw tmp;
	sljit_uw imm;
	sljit_sw inst = VSTR_F32 | (flags & (SLJIT_SINGLE_OP | FPU_LOAD));
	SLJIT_ASSERT(arg & SLJIT_MEM);

	if (SLJIT_UNLIKELY(arg & OFFS_REG_MASK)) {
		FAIL_IF(push_inst(compiler, EMIT_DATA_PROCESS_INS(ADD_DP, 0, TMP_REG1, arg & REG_MASK, RM(OFFS_REG(arg)) | ((argw & 0x3) << 7))));
		arg = SLJIT_MEM | TMP_REG1;
		argw = 0;
	}

	/* Fast loads and stores. */
	if ((arg & REG_MASK)) {
		if (!(argw & ~0x3fc))
			return push_inst(compiler, EMIT_FPU_DATA_TRANSFER(inst, 1, arg & REG_MASK, reg, argw >> 2));
		if (!(-argw & ~0x3fc))
			return push_inst(compiler, EMIT_FPU_DATA_TRANSFER(inst, 0, arg & REG_MASK, reg, (-argw) >> 2));
	}

	if (compiler->cache_arg == arg) {
		tmp = argw - compiler->cache_argw;
		if (!(tmp & ~0x3fc))
			return push_inst(compiler, EMIT_FPU_DATA_TRANSFER(inst, 1, TMP_REG3, reg, tmp >> 2));
		if (!(-tmp & ~0x3fc))
			return push_inst(compiler, EMIT_FPU_DATA_TRANSFER(inst, 0, TMP_REG3, reg, -tmp >> 2));
		if (emit_set_delta(compiler, TMP_REG3, TMP_REG3, tmp) != SLJIT_ERR_UNSUPPORTED) {
			FAIL_IF(compiler->error);
			compiler->cache_argw = argw;
			return push_inst(compiler, EMIT_FPU_DATA_TRANSFER(inst, 1, TMP_REG3, reg, 0));
		}
	}

	if (arg & REG_MASK) {
		if (emit_set_delta(compiler, TMP_REG1, arg & REG_MASK, argw) != SLJIT_ERR_UNSUPPORTED) {
			FAIL_IF(compiler->error);
			return push_inst(compiler, EMIT_FPU_DATA_TRANSFER(inst, 1, TMP_REG1, reg, 0));
		}
		imm = get_imm(argw & ~0x3fc);
		if (imm) {
			FAIL_IF(push_inst(compiler, EMIT_DATA_PROCESS_INS(ADD_DP, 0, TMP_REG1, arg & REG_MASK, imm)));
			return push_inst(compiler, EMIT_FPU_DATA_TRANSFER(inst, 1, TMP_REG1, reg, (argw & 0x3fc) >> 2));
		}
		imm = get_imm(-argw & ~0x3fc);
		if (imm) {
			argw = -argw;
			FAIL_IF(push_inst(compiler, EMIT_DATA_PROCESS_INS(SUB_DP, 0, TMP_REG1, arg & REG_MASK, imm)));
			return push_inst(compiler, EMIT_FPU_DATA_TRANSFER(inst, 0, TMP_REG1, reg, (argw & 0x3fc) >> 2));
		}
	}

	compiler->cache_arg = arg;
	compiler->cache_argw = argw;
	if (arg & REG_MASK) {
		FAIL_IF(load_immediate(compiler, TMP_REG1, argw));
		FAIL_IF(push_inst(compiler, EMIT_DATA_PROCESS_INS(ADD_DP, 0, TMP_REG3, arg & REG_MASK, reg_map[TMP_REG1])));
	}
	else
		FAIL_IF(load_immediate(compiler, TMP_REG3, argw));

	return push_inst(compiler, EMIT_FPU_DATA_TRANSFER(inst, 1, TMP_REG3, reg, 0));
}

static SLJIT_INLINE sljit_si sljit_emit_fop1_convw_fromd(struct sljit_compiler *compiler, sljit_si op,
	sljit_si dst, sljit_sw dstw,
	sljit_si src, sljit_sw srcw)
{
	if (src & SLJIT_MEM) {
		FAIL_IF(emit_fop_mem(compiler, (op & SLJIT_SINGLE_OP) | FPU_LOAD, TMP_FREG1, src, srcw));
		src = TMP_FREG1;
	}

	FAIL_IF(push_inst(compiler, EMIT_FPU_OPERATION(VCVT_S32_F32, op & SLJIT_SINGLE_OP, TMP_FREG1, src, 0)));

	if (dst == SLJIT_UNUSED)
		return SLJIT_SUCCESS;

	if (FAST_IS_REG(dst))
		return push_inst(compiler, VMOV | (1 << 20) | RD(dst) | (TMP_FREG1 << 16));

	/* Store the integer value from a VFP register. */
	return emit_fop_mem(compiler, 0, TMP_FREG1, dst, dstw);
}

static SLJIT_INLINE sljit_si sljit_emit_fop1_convd_fromw(struct sljit_compiler *compiler, sljit_si op,
	sljit_si dst, sljit_sw dstw,
	sljit_si src, sljit_sw srcw)
{
	sljit_si dst_r = FAST_IS_REG(dst) ? dst : TMP_FREG1;

	if (FAST_IS_REG(src))
		FAIL_IF(push_inst(compiler, VMOV | RD(src) | (TMP_FREG1 << 16)));
	else if (src & SLJIT_MEM) {
		/* Load the integer value into a VFP register. */
		FAIL_IF(emit_fop_mem(compiler, FPU_LOAD, TMP_FREG1, src, srcw));
	}
	else {
		FAIL_IF(load_immediate(compiler, TMP_REG1, srcw));
		FAIL_IF(push_inst(compiler, VMOV | RD(TMP_REG1) | (TMP_FREG1 << 16)));
	}

	FAIL_IF(push_inst(compiler, EMIT_FPU_OPERATION(VCVT_F32_S32, op & SLJIT_SINGLE_OP, dst_r, TMP_FREG1, 0)));

	if (dst & SLJIT_MEM)
		return emit_fop_mem(compiler, (op & SLJIT_SINGLE_OP), TMP_FREG1, dst, dstw);
	return SLJIT_SUCCESS;
}

static SLJIT_INLINE sljit_si sljit_emit_fop1_cmp(struct sljit_compiler *compiler, sljit_si op,
	sljit_si src1, sljit_sw src1w,
	sljit_si src2, sljit_sw src2w)
{
	if (src1 & SLJIT_MEM) {
		FAIL_IF(emit_fop_mem(compiler, (op & SLJIT_SINGLE_OP) | FPU_LOAD, TMP_FREG1, src1, src1w));
		src1 = TMP_FREG1;
	}

	if (src2 & SLJIT_MEM) {
		FAIL_IF(emit_fop_mem(compiler, (op & SLJIT_SINGLE_OP) | FPU_LOAD, TMP_FREG2, src2, src2w));
		src2 = TMP_FREG2;
	}

	FAIL_IF(push_inst(compiler, EMIT_FPU_OPERATION(VCMP_F32, op & SLJIT_SINGLE_OP, src1, src2, 0)));
	return push_inst(compiler, VMRS);
}

SLJIT_API_FUNC_ATTRIBUTE sljit_si sljit_emit_fop1(struct sljit_compiler *compiler, sljit_si op,
	sljit_si dst, sljit_sw dstw,
	sljit_si src, sljit_sw srcw)
{
	sljit_si dst_r;

	CHECK_ERROR();
	compiler->cache_arg = 0;
	compiler->cache_argw = 0;
	if (GET_OPCODE(op) != SLJIT_CONVD_FROMS)
		op ^= SLJIT_SINGLE_OP;

	SLJIT_COMPILE_ASSERT((SLJIT_SINGLE_OP == 0x100), float_transfer_bit_error);
	SELECT_FOP1_OPERATION_WITH_CHECKS(compiler, op, dst, dstw, src, srcw);

	dst_r = FAST_IS_REG(dst) ? dst : TMP_FREG1;

	if (src & SLJIT_MEM) {
		FAIL_IF(emit_fop_mem(compiler, (op & SLJIT_SINGLE_OP) | FPU_LOAD, dst_r, src, srcw));
		src = dst_r;
	}

	switch (GET_OPCODE(op)) {
	case SLJIT_DMOV:
		if (src != dst_r) {
			if (dst_r != TMP_FREG1)
				FAIL_IF(push_inst(compiler, EMIT_FPU_OPERATION(VMOV_F32, op & SLJIT_SINGLE_OP, dst_r, src, 0)));
			else
				dst_r = src;
		}
		break;
	case SLJIT_DNEG:
		FAIL_IF(push_inst(compiler, EMIT_FPU_OPERATION(VNEG_F32, op & SLJIT_SINGLE_OP, dst_r, src, 0)));
		break;
	case SLJIT_DABS:
		FAIL_IF(push_inst(compiler, EMIT_FPU_OPERATION(VABS_F32, op & SLJIT_SINGLE_OP, dst_r, src, 0)));
		break;
	case SLJIT_CONVD_FROMS:
		FAIL_IF(push_inst(compiler, EMIT_FPU_OPERATION(VCVT_F64_F32, op & SLJIT_SINGLE_OP, dst_r, src, 0)));
		op ^= SLJIT_SINGLE_OP;
		break;
	}

	if (dst & SLJIT_MEM)
		return emit_fop_mem(compiler, (op & SLJIT_SINGLE_OP), dst_r, dst, dstw);
	return SLJIT_SUCCESS;
}

SLJIT_API_FUNC_ATTRIBUTE sljit_si sljit_emit_fop2(struct sljit_compiler *compiler, sljit_si op,
	sljit_si dst, sljit_sw dstw,
	sljit_si src1, sljit_sw src1w,
	sljit_si src2, sljit_sw src2w)
{
	sljit_si dst_r;

	CHECK_ERROR();
	CHECK(check_sljit_emit_fop2(compiler, op, dst, dstw, src1, src1w, src2, src2w));
	ADJUST_LOCAL_OFFSET(dst, dstw);
	ADJUST_LOCAL_OFFSET(src1, src1w);
	ADJUST_LOCAL_OFFSET(src2, src2w);

	compiler->cache_arg = 0;
	compiler->cache_argw = 0;
	op ^= SLJIT_SINGLE_OP;

	dst_r = FAST_IS_REG(dst) ? dst : TMP_FREG1;

	if (src2 & SLJIT_MEM) {
		FAIL_IF(emit_fop_mem(compiler, (op & SLJIT_SINGLE_OP) | FPU_LOAD, TMP_FREG2, src2, src2w));
		src2 = TMP_FREG2;
	}

	if (src1 & SLJIT_MEM) {
		FAIL_IF(emit_fop_mem(compiler, (op & SLJIT_SINGLE_OP) | FPU_LOAD, TMP_FREG1, src1, src1w));
		src1 = TMP_FREG1;
	}

	switch (GET_OPCODE(op)) {
	case SLJIT_DADD:
		FAIL_IF(push_inst(compiler, EMIT_FPU_OPERATION(VADD_F32, op & SLJIT_SINGLE_OP, dst_r, src2, src1)));
		break;

	case SLJIT_DSUB:
		FAIL_IF(push_inst(compiler, EMIT_FPU_OPERATION(VSUB_F32, op & SLJIT_SINGLE_OP, dst_r, src2, src1)));
		break;

	case SLJIT_DMUL:
		FAIL_IF(push_inst(compiler, EMIT_FPU_OPERATION(VMUL_F32, op & SLJIT_SINGLE_OP, dst_r, src2, src1)));
		break;

	case SLJIT_DDIV:
		FAIL_IF(push_inst(compiler, EMIT_FPU_OPERATION(VDIV_F32, op & SLJIT_SINGLE_OP, dst_r, src2, src1)));
		break;
	}

	if (dst_r == TMP_FREG1)
		FAIL_IF(emit_fop_mem(compiler, (op & SLJIT_SINGLE_OP), TMP_FREG1, dst, dstw));

	return SLJIT_SUCCESS;
}

#undef FPU_LOAD
#undef EMIT_FPU_DATA_TRANSFER
#undef EMIT_FPU_OPERATION

/* --------------------------------------------------------------------- */
/*  Other instructions                                                   */
/* --------------------------------------------------------------------- */

SLJIT_API_FUNC_ATTRIBUTE sljit_si sljit_emit_fast_enter(struct sljit_compiler *compiler, sljit_si dst, sljit_sw dstw)
{
	CHECK_ERROR();
	CHECK(check_sljit_emit_fast_enter(compiler, dst, dstw));
	ADJUST_LOCAL_OFFSET(dst, dstw);

	/* For UNUSED dst. Uncommon, but possible. */
	if (dst == SLJIT_UNUSED)
		return SLJIT_SUCCESS;

	if (FAST_IS_REG(dst))
		return push_inst(compiler, EMIT_DATA_PROCESS_INS(MOV_DP, 0, dst, SLJIT_UNUSED, RM(TMP_REG3)));

	/* Memory. */
	if (getput_arg_fast(compiler, WORD_DATA, TMP_REG3, dst, dstw))
		return compiler->error;
	/* TMP_REG3 is used for caching. */
	FAIL_IF(push_inst(compiler, EMIT_DATA_PROCESS_INS(MOV_DP, 0, TMP_REG2, SLJIT_UNUSED, RM(TMP_REG3))));
	compiler->cache_arg = 0;
	compiler->cache_argw = 0;
	return getput_arg(compiler, WORD_DATA, TMP_REG2, dst, dstw, 0, 0);
}

SLJIT_API_FUNC_ATTRIBUTE sljit_si sljit_emit_fast_return(struct sljit_compiler *compiler, sljit_si src, sljit_sw srcw)
{
	CHECK_ERROR();
	CHECK(check_sljit_emit_fast_return(compiler, src, srcw));
	ADJUST_LOCAL_OFFSET(src, srcw);

	if (FAST_IS_REG(src))
		FAIL_IF(push_inst(compiler, EMIT_DATA_PROCESS_INS(MOV_DP, 0, TMP_REG3, SLJIT_UNUSED, RM(src))));
	else if (src & SLJIT_MEM) {
		if (getput_arg_fast(compiler, WORD_DATA | LOAD_DATA, TMP_REG3, src, srcw))
			FAIL_IF(compiler->error);
		else {
			compiler->cache_arg = 0;
			compiler->cache_argw = 0;
			FAIL_IF(getput_arg(compiler, WORD_DATA | LOAD_DATA, TMP_REG2, src, srcw, 0, 0));
			FAIL_IF(push_inst(compiler, EMIT_DATA_PROCESS_INS(MOV_DP, 0, TMP_REG3, SLJIT_UNUSED, RM(TMP_REG2))));
		}
	}
	else if (src & SLJIT_IMM)
		FAIL_IF(load_immediate(compiler, TMP_REG3, srcw));
	return push_inst(compiler, BLX | RM(TMP_REG3));
}

/* --------------------------------------------------------------------- */
/*  Conditional instructions                                             */
/* --------------------------------------------------------------------- */

static sljit_uw get_cc(sljit_si type)
{
	switch (type) {
	case SLJIT_EQUAL:
	case SLJIT_MUL_NOT_OVERFLOW:
	case SLJIT_D_EQUAL:
		return 0x00000000;

	case SLJIT_NOT_EQUAL:
	case SLJIT_MUL_OVERFLOW:
	case SLJIT_D_NOT_EQUAL:
		return 0x10000000;

	case SLJIT_LESS:
	case SLJIT_D_LESS:
		return 0x30000000;

	case SLJIT_GREATER_EQUAL:
	case SLJIT_D_GREATER_EQUAL:
		return 0x20000000;

	case SLJIT_GREATER:
	case SLJIT_D_GREATER:
		return 0x80000000;

	case SLJIT_LESS_EQUAL:
	case SLJIT_D_LESS_EQUAL:
		return 0x90000000;

	case SLJIT_SIG_LESS:
		return 0xb0000000;

	case SLJIT_SIG_GREATER_EQUAL:
		return 0xa0000000;

	case SLJIT_SIG_GREATER:
		return 0xc0000000;

	case SLJIT_SIG_LESS_EQUAL:
		return 0xd0000000;

	case SLJIT_OVERFLOW:
	case SLJIT_D_UNORDERED:
		return 0x60000000;

	case SLJIT_NOT_OVERFLOW:
	case SLJIT_D_ORDERED:
		return 0x70000000;

	default:
		SLJIT_ASSERT(type >= SLJIT_JUMP && type <= SLJIT_CALL3);
		return 0xe0000000;
	}
}

SLJIT_API_FUNC_ATTRIBUTE struct sljit_label* sljit_emit_label(struct sljit_compiler *compiler)
{
	struct sljit_label *label;

	CHECK_ERROR_PTR();
	CHECK_PTR(check_sljit_emit_label(compiler));

	if (compiler->last_label && compiler->last_label->size == compiler->size)
		return compiler->last_label;

	label = (struct sljit_label*)ensure_abuf(compiler, sizeof(struct sljit_label));
	PTR_FAIL_IF(!label);
	set_label(label, compiler);
	return label;
}

SLJIT_API_FUNC_ATTRIBUTE struct sljit_jump* sljit_emit_jump(struct sljit_compiler *compiler, sljit_si type)
{
	struct sljit_jump *jump;

	CHECK_ERROR_PTR();
	CHECK_PTR(check_sljit_emit_jump(compiler, type));

	jump = (struct sljit_jump*)ensure_abuf(compiler, sizeof(struct sljit_jump));
	PTR_FAIL_IF(!jump);
	set_jump(jump, compiler, type & SLJIT_REWRITABLE_JUMP);
	type &= 0xff;

	/* In ARM, we don't need to touch the arguments. */
#if (defined SLJIT_CONFIG_ARM_V5 && SLJIT_CONFIG_ARM_V5)
	if (type >= SLJIT_FAST_CALL)
		PTR_FAIL_IF(prepare_blx(compiler));
	PTR_FAIL_IF(push_inst_with_unique_literal(compiler, ((EMIT_DATA_TRANSFER(WORD_DATA | LOAD_DATA, 1, 0,
		type <= SLJIT_JUMP ? TMP_PC : TMP_REG1, TMP_PC, 0)) & ~COND_MASK) | get_cc(type), 0));

	if (jump->flags & SLJIT_REWRITABLE_JUMP) {
		jump->addr = compiler->size;
		compiler->patches++;
	}

	if (type >= SLJIT_FAST_CALL) {
		jump->flags |= IS_BL;
		PTR_FAIL_IF(emit_blx(compiler));
	}

	if (!(jump->flags & SLJIT_REWRITABLE_JUMP))
		jump->addr = compiler->size;
#else
	if (type >= SLJIT_FAST_CALL)
		jump->flags |= IS_BL;
	PTR_FAIL_IF(emit_imm(compiler, TMP_REG1, 0));
	PTR_FAIL_IF(push_inst(compiler, (((type <= SLJIT_JUMP ? BX : BLX) | RM(TMP_REG1)) & ~COND_MASK) | get_cc(type)));
	jump->addr = compiler->size;
#endif
	return jump;
}

SLJIT_API_FUNC_ATTRIBUTE sljit_si sljit_emit_ijump(struct sljit_compiler *compiler, sljit_si type, sljit_si src, sljit_sw srcw)
{
	struct sljit_jump *jump;

	CHECK_ERROR();
	CHECK(check_sljit_emit_ijump(compiler, type, src, srcw));
	ADJUST_LOCAL_OFFSET(src, srcw);

	/* In ARM, we don't need to touch the arguments. */
	if (!(src & SLJIT_IMM)) {
		if (FAST_IS_REG(src))
			return push_inst(compiler, (type <= SLJIT_JUMP ? BX : BLX) | RM(src));

		SLJIT_ASSERT(src & SLJIT_MEM);
		FAIL_IF(emit_op_mem(compiler, WORD_DATA | LOAD_DATA, TMP_REG2, src, srcw));
		return push_inst(compiler, (type <= SLJIT_JUMP ? BX : BLX) | RM(TMP_REG2));
	}

	jump = (struct sljit_jump*)ensure_abuf(compiler, sizeof(struct sljit_jump));
	FAIL_IF(!jump);
	set_jump(jump, compiler, JUMP_ADDR | ((type >= SLJIT_FAST_CALL) ? IS_BL : 0));
	jump->u.target = srcw;

#if (defined SLJIT_CONFIG_ARM_V5 && SLJIT_CONFIG_ARM_V5)
	if (type >= SLJIT_FAST_CALL)
		FAIL_IF(prepare_blx(compiler));
	FAIL_IF(push_inst_with_unique_literal(compiler, EMIT_DATA_TRANSFER(WORD_DATA | LOAD_DATA, 1, 0, type <= SLJIT_JUMP ? TMP_PC : TMP_REG1, TMP_PC, 0), 0));
	if (type >= SLJIT_FAST_CALL)
		FAIL_IF(emit_blx(compiler));
#else
	FAIL_IF(emit_imm(compiler, TMP_REG1, 0));
	FAIL_IF(push_inst(compiler, (type <= SLJIT_JUMP ? BX : BLX) | RM(TMP_REG1)));
#endif
	jump->addr = compiler->size;
	return SLJIT_SUCCESS;
}

SLJIT_API_FUNC_ATTRIBUTE sljit_si sljit_emit_op_flags(struct sljit_compiler *compiler, sljit_si op,
	sljit_si dst, sljit_sw dstw,
	sljit_si src, sljit_sw srcw,
	sljit_si type)
{
	sljit_si dst_r, flags = GET_ALL_FLAGS(op);
	sljit_uw cc, ins;

	CHECK_ERROR();
	CHECK(check_sljit_emit_op_flags(compiler, op, dst, dstw, src, srcw, type));
	ADJUST_LOCAL_OFFSET(dst, dstw);
	ADJUST_LOCAL_OFFSET(src, srcw);

	if (dst == SLJIT_UNUSED)
		return SLJIT_SUCCESS;

	op = GET_OPCODE(op);
	cc = get_cc(type & 0xff);
	dst_r = FAST_IS_REG(dst) ? dst : TMP_REG2;

	if (op < SLJIT_ADD) {
		FAIL_IF(push_inst(compiler, EMIT_DATA_PROCESS_INS(MOV_DP, 0, dst_r, SLJIT_UNUSED, SRC2_IMM | 0)));
		FAIL_IF(push_inst(compiler, (EMIT_DATA_PROCESS_INS(MOV_DP, 0, dst_r, SLJIT_UNUSED, SRC2_IMM | 1) & ~COND_MASK) | cc));
		return (dst_r == TMP_REG2) ? emit_op_mem(compiler, WORD_DATA, TMP_REG2, dst, dstw) : SLJIT_SUCCESS;
	}

	ins = (op == SLJIT_AND ? AND_DP : (op == SLJIT_OR ? ORR_DP : EOR_DP));
	if ((op == SLJIT_OR || op == SLJIT_XOR) && FAST_IS_REG(dst) && dst == src) {
		FAIL_IF(push_inst(compiler, (EMIT_DATA_PROCESS_INS(ins, 0, dst, dst, SRC2_IMM | 1) & ~COND_MASK) | cc));
		/* The condition must always be set, even if the ORR/EOR is not executed above. */
		return (flags & SLJIT_SET_E) ? push_inst(compiler, EMIT_DATA_PROCESS_INS(MOV_DP, SET_FLAGS, TMP_REG1, SLJIT_UNUSED, RM(dst))) : SLJIT_SUCCESS;
	}

	compiler->cache_arg = 0;
	compiler->cache_argw = 0;
	if (src & SLJIT_MEM) {
		FAIL_IF(emit_op_mem2(compiler, WORD_DATA | LOAD_DATA, TMP_REG1, src, srcw, dst, dstw));
		src = TMP_REG1;
		srcw = 0;
	} else if (src & SLJIT_IMM) {
		FAIL_IF(load_immediate(compiler, TMP_REG1, srcw));
		src = TMP_REG1;
		srcw = 0;
	}

	FAIL_IF(push_inst(compiler, (EMIT_DATA_PROCESS_INS(ins, 0, dst_r, src, SRC2_IMM | 1) & ~COND_MASK) | cc));
	FAIL_IF(push_inst(compiler, (EMIT_DATA_PROCESS_INS(ins, 0, dst_r, src, SRC2_IMM | 0) & ~COND_MASK) | (cc ^ 0x10000000)));
	if (dst_r == TMP_REG2)
		FAIL_IF(emit_op_mem2(compiler, WORD_DATA, TMP_REG2, dst, dstw, 0, 0));

	return (flags & SLJIT_SET_E) ? push_inst(compiler, EMIT_DATA_PROCESS_INS(MOV_DP, SET_FLAGS, TMP_REG1, SLJIT_UNUSED, RM(dst_r))) : SLJIT_SUCCESS;
}

SLJIT_API_FUNC_ATTRIBUTE struct sljit_const* sljit_emit_const(struct sljit_compiler *compiler, sljit_si dst, sljit_sw dstw, sljit_sw init_value)
{
	struct sljit_const *const_;
	sljit_si reg;

	CHECK_ERROR_PTR();
	CHECK_PTR(check_sljit_emit_const(compiler, dst, dstw, init_value));
	ADJUST_LOCAL_OFFSET(dst, dstw);

	const_ = (struct sljit_const*)ensure_abuf(compiler, sizeof(struct sljit_const));
	PTR_FAIL_IF(!const_);

	reg = SLOW_IS_REG(dst) ? dst : TMP_REG2;

#if (defined SLJIT_CONFIG_ARM_V5 && SLJIT_CONFIG_ARM_V5)
	PTR_FAIL_IF(push_inst_with_unique_literal(compiler, EMIT_DATA_TRANSFER(WORD_DATA | LOAD_DATA, 1, 0, reg, TMP_PC, 0), init_value));
	compiler->patches++;
#else
	PTR_FAIL_IF(emit_imm(compiler, reg, init_value));
#endif
	set_const(const_, compiler);

	if (dst & SLJIT_MEM)
		PTR_FAIL_IF(emit_op_mem(compiler, WORD_DATA, TMP_REG2, dst, dstw));
	return const_;
}

SLJIT_API_FUNC_ATTRIBUTE void sljit_set_jump_addr(sljit_uw addr, sljit_uw new_addr)
{
	inline_set_jump_addr(addr, new_addr, 1);
}

SLJIT_API_FUNC_ATTRIBUTE void sljit_set_const(sljit_uw addr, sljit_sw new_constant)
{
	inline_set_const(addr, new_constant, 1);
}


<|start_filename|>ext/opcache/zend_accelerator_util_funcs.c<|end_filename|>
/*
   +----------------------------------------------------------------------+
   | Zend OPcache                                                         |
   +----------------------------------------------------------------------+
   | Copyright (c) 1998-2015 The PHP Group                                |
   +----------------------------------------------------------------------+
   | This source file is subject to version 3.01 of the PHP 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.php.net/license/3_01.txt                                  |
   | If you did not receive a copy of the PHP license and are unable to   |
   | obtain it through the world-wide-web, please send a note to          |
   | <EMAIL> so we can mail you a copy immediately.               |
   +----------------------------------------------------------------------+
   | Authors: <NAME> <<EMAIL>>                                |
   |          <NAME> <<EMAIL>>                                |
   |          <NAME> <<EMAIL>>                          |
   |          <NAME> <<EMAIL>>                             |
   +----------------------------------------------------------------------+
*/

#include "zend_API.h"
#include "zend_constants.h"
#include "zend_accelerator_util_funcs.h"
#include "zend_persist.h"
#include "zend_shared_alloc.h"

#define ZEND_PROTECTED_REFCOUNT	(1<<30)

static zend_uint zend_accel_refcount = ZEND_PROTECTED_REFCOUNT;

#if SIZEOF_SIZE_T <= SIZEOF_LONG
/* If sizeof(void*) == sizeof(ulong) we can use zend_hash index functions */
# define accel_xlat_set(old, new)	zend_hash_index_update(&ZCG(bind_hash), (ulong)(zend_uintptr_t)(old), &(new), sizeof(void*), NULL)
# define accel_xlat_get(old, new)	zend_hash_index_find(&ZCG(bind_hash), (ulong)(zend_uintptr_t)(old), (void**)&(new))
#else
# define accel_xlat_set(old, new)	zend_hash_quick_add(&ZCG(bind_hash), (char*)&(old), sizeof(void*), (ulong)(zend_uintptr_t)(old), (void**)&(new), sizeof(void*), NULL)
# define accel_xlat_get(old, new)	zend_hash_quick_find(&ZCG(bind_hash), (char*)&(old), sizeof(void*), (ulong)(zend_uintptr_t)(old), (void**)&(new))
#endif

typedef int (*id_function_t)(void *, void *);
typedef void (*unique_copy_ctor_func_t)(void *pElement);

#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO
static const Bucket *uninitialized_bucket = NULL;
#endif

static int zend_prepare_function_for_execution(zend_op_array *op_array);
static void zend_hash_clone_zval(HashTable *ht, HashTable *source, int bind);

static void zend_accel_destroy_zend_function(zend_function *function)
{
	TSRMLS_FETCH();

	if (function->type == ZEND_USER_FUNCTION) {
		if (function->op_array.static_variables) {

			efree(function->op_array.static_variables);
			function->op_array.static_variables = NULL;
		}
	}

	destroy_zend_function(function TSRMLS_CC);
}

static void zend_accel_destroy_zend_class(zend_class_entry **pce)
{
	zend_class_entry *ce = *pce;

	ce->function_table.pDestructor = (dtor_func_t) zend_accel_destroy_zend_function;
	destroy_zend_class(pce);
}

zend_persistent_script* create_persistent_script(void)
{
	zend_persistent_script *persistent_script = (zend_persistent_script *) emalloc(sizeof(zend_persistent_script));
	memset(persistent_script, 0, sizeof(zend_persistent_script));

	zend_hash_init(&persistent_script->function_table, 100, NULL, (dtor_func_t) zend_accel_destroy_zend_function, 0);
	/* class_table is usually destroyed by free_persistent_script() that
	 * overrides destructor. ZEND_CLASS_DTOR may be used by standard
	 * PHP compiler
	 */
	zend_hash_init(&persistent_script->class_table, 10, NULL, ZEND_CLASS_DTOR, 0);

	return persistent_script;
}

static int compact_hash_table(HashTable *ht)
{
	uint i = 3;
	uint nSize;
	Bucket **t;

	if (!ht->nNumOfElements) {
		/* Empty tables don't allocate space for Buckets */
		return 1;
	}

	if (ht->nNumOfElements >= 0x80000000) {
		/* prevent overflow */
		nSize = 0x80000000;
	} else {
		while ((1U << i) < ht->nNumOfElements) {
			i++;
		}
		nSize = 1 << i;
	}

	if (nSize >= ht->nTableSize) {
		/* Keep the size */
		return 1;
	}

	t = (Bucket **)pemalloc(nSize * sizeof(Bucket *), ht->persistent);
	if (!t) {
		return 0;
	}

	pefree(ht->arBuckets, ht->persistent);

	ht->arBuckets = t;
	ht->nTableSize = nSize;
	ht->nTableMask = ht->nTableSize - 1;
	zend_hash_rehash(ht);
	
	return 1;
}

int compact_persistent_script(zend_persistent_script *persistent_script)
{
	return compact_hash_table(&persistent_script->function_table) &&
	       compact_hash_table(&persistent_script->class_table);
}

void free_persistent_script(zend_persistent_script *persistent_script, int destroy_elements)
{
	if (destroy_elements) {
		persistent_script->function_table.pDestructor = (dtor_func_t)zend_accel_destroy_zend_function;
		persistent_script->class_table.pDestructor = (dtor_func_t)zend_accel_destroy_zend_class;
	} else {
		persistent_script->function_table.pDestructor = NULL;
		persistent_script->class_table.pDestructor = NULL;
	}

	zend_hash_destroy(&persistent_script->function_table);
	zend_hash_destroy(&persistent_script->class_table);

	if (persistent_script->full_path) {
		efree(persistent_script->full_path);
	}

	efree(persistent_script);
}

static int is_not_internal_function(zend_function *function)
{
	return(function->type != ZEND_INTERNAL_FUNCTION);
}

void zend_accel_free_user_functions(HashTable *ht TSRMLS_DC)
{
	dtor_func_t orig_dtor = ht->pDestructor;

	ht->pDestructor = NULL;
	zend_hash_apply(ht, (apply_func_t) is_not_internal_function TSRMLS_CC);
	ht->pDestructor = orig_dtor;
}

static int move_user_function(zend_function *function
#if ZEND_EXTENSION_API_NO >= PHP_5_3_X_API_NO
	TSRMLS_DC 
#endif
	, int num_args, va_list args, zend_hash_key *hash_key) 
{
	HashTable *function_table = va_arg(args, HashTable *);
	(void)num_args; /* keep the compiler happy */
#if ZEND_EXTENSION_API_NO < PHP_5_3_X_API_NO
	TSRMLS_FETCH();
#endif 

	if (function->type == ZEND_USER_FUNCTION) {
		zend_hash_quick_update(function_table, hash_key->arKey, hash_key->nKeyLength, hash_key->h, function, sizeof(zend_function), NULL);
		return 1;
	} else {
		return 0;
	}
}

void zend_accel_move_user_functions(HashTable *src, HashTable *dst TSRMLS_DC)
{
	dtor_func_t orig_dtor = src->pDestructor;

	src->pDestructor = NULL;
#if ZEND_EXTENSION_API_NO < PHP_5_3_X_API_NO
	zend_hash_apply_with_arguments(src, (apply_func_args_t)move_user_function, 1, dst);
#else
	zend_hash_apply_with_arguments(src TSRMLS_CC, (apply_func_args_t)move_user_function, 1, dst);
#endif 
	src->pDestructor = orig_dtor;
}

static int copy_internal_function(zend_function *function, HashTable *function_table TSRMLS_DC)
{
	if (function->type == ZEND_INTERNAL_FUNCTION) {
		zend_hash_update(function_table, function->common.function_name, strlen(function->common.function_name) + 1, function, sizeof(zend_function), NULL);
	}
	return 0;
}

void zend_accel_copy_internal_functions(TSRMLS_D)
{
	zend_hash_apply_with_argument(CG(function_table), (apply_func_arg_t)copy_internal_function, &ZCG(function_table) TSRMLS_CC);
	ZCG(internal_functions_count) = zend_hash_num_elements(&ZCG(function_table));
}

static void zend_destroy_property_info(zend_property_info *property_info)
{
	interned_efree((char*)property_info->name);
	if (property_info->doc_comment) {
		efree((char*)property_info->doc_comment);
	}
}

static inline zval* zend_clone_zval(zval *src, int bind TSRMLS_DC)
{
	zval *ret, **ret_ptr = NULL;

	if (!bind) {
		ALLOC_ZVAL(ret);
		*ret = *src;
		INIT_PZVAL(ret);
	} else if (Z_REFCOUNT_P(src) == 1) {
		ALLOC_ZVAL(ret);
		*ret = *src;
	} else if (accel_xlat_get(src, ret_ptr) != SUCCESS) {
		ALLOC_ZVAL(ret);
		*ret = *src;
		accel_xlat_set(src, ret);
	} else {
		return *ret_ptr;
	}

#if ZEND_EXTENSION_API_NO >= PHP_5_3_X_API_NO
	if ((Z_TYPE_P(ret) & IS_CONSTANT_TYPE_MASK) >= IS_ARRAY) {
		switch ((Z_TYPE_P(ret) & IS_CONSTANT_TYPE_MASK)) {
#else
	if ((Z_TYPE_P(ret) & ~IS_CONSTANT_INDEX) >= IS_ARRAY) {
		switch ((Z_TYPE_P(ret) & ~IS_CONSTANT_INDEX)) {
#endif
			case IS_STRING:
		    case IS_CONSTANT:
				Z_STRVAL_P(ret) = (char *) interned_estrndup(Z_STRVAL_P(ret), Z_STRLEN_P(ret));
				break;
			case IS_ARRAY:
		    case IS_CONSTANT_ARRAY:
				if (ret->value.ht && ret->value.ht != &EG(symbol_table)) {
					ALLOC_HASHTABLE(ret->value.ht);
					zend_hash_clone_zval(ret->value.ht, src->value.ht, 0);
				}
				break;
		}
	}
	return ret;
}

static void zend_hash_clone_zval(HashTable *ht, HashTable *source, int bind)
{
	Bucket *p, *q, **prev;
	ulong nIndex;
	zval *ppz;
	TSRMLS_FETCH();

	ht->nTableSize = source->nTableSize;
	ht->nTableMask = source->nTableMask;
	ht->nNumOfElements = source->nNumOfElements;
	ht->nNextFreeElement = source->nNextFreeElement;
	ht->pDestructor = ZVAL_PTR_DTOR;
#if ZEND_DEBUG
	ht->inconsistent = 0;
#endif
	ht->persistent = 0;
	ht->arBuckets = NULL;
	ht->pListHead = NULL;
	ht->pListTail = NULL;
	ht->pInternalPointer = NULL;
	ht->nApplyCount = 0;
	ht->bApplyProtection = 1;

#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO
	if (!ht->nTableMask) {
		ht->arBuckets = (Bucket**)&uninitialized_bucket;
		return;
	}
#endif

	ht->arBuckets = (Bucket **) ecalloc(ht->nTableSize, sizeof(Bucket *));

	prev = &ht->pListHead;
	p = source->pListHead;
	while (p) {
		nIndex = p->h & ht->nTableMask;

		/* Create bucket and initialize key */
#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO
		if (!p->nKeyLength) {
			q = (Bucket *) emalloc(sizeof(Bucket));
			q->arKey = NULL;
		} else if (IS_INTERNED(p->arKey)) {
			q = (Bucket *) emalloc(sizeof(Bucket));
			q->arKey = p->arKey;
		} else {
			q = (Bucket *) emalloc(sizeof(Bucket) + p->nKeyLength);
			q->arKey = ((char*)q) + sizeof(Bucket);
			memcpy((char*)q->arKey, p->arKey, p->nKeyLength);
		}
#else
		q = (Bucket *) emalloc(sizeof(Bucket) - 1 + p->nKeyLength);
		if (p->nKeyLength) {
			memcpy(q->arKey, p->arKey, p->nKeyLength);
		}
#endif
		q->h = p->h;
		q->nKeyLength = p->nKeyLength;

		/* Insert into hash collision list */
		q->pNext = ht->arBuckets[nIndex];
		q->pLast = NULL;
		if (q->pNext) {
			q->pNext->pLast = q;
		}
		ht->arBuckets[nIndex] = q;

		/* Insert into global list */
		q->pListLast = ht->pListTail;
		ht->pListTail = q;
		q->pListNext = NULL;
		*prev = q;
		prev = &q->pListNext;

		/* Copy data */
		q->pData = &q->pDataPtr;
		if (!bind) {
			ALLOC_ZVAL(ppz);
			*ppz = *((zval*)p->pDataPtr);
			INIT_PZVAL(ppz);
		} else if (Z_REFCOUNT_P((zval*)p->pDataPtr) == 1) {
			ALLOC_ZVAL(ppz);
			*ppz = *((zval*)p->pDataPtr);
		} else if (accel_xlat_get(p->pDataPtr, ppz) != SUCCESS) {
			ALLOC_ZVAL(ppz);
			*ppz = *((zval*)p->pDataPtr);
			accel_xlat_set(p->pDataPtr, ppz);
		} else {
			q->pDataPtr = *(void**)ppz;
			p = p->pListNext;
			continue;
		}
		q->pDataPtr = (void*)ppz;

#if ZEND_EXTENSION_API_NO >= PHP_5_3_X_API_NO
		if ((Z_TYPE_P((zval*)p->pDataPtr) & IS_CONSTANT_TYPE_MASK) >= IS_ARRAY) {
			switch ((Z_TYPE_P((zval*)p->pDataPtr) & IS_CONSTANT_TYPE_MASK)) {
#else
		if ((Z_TYPE_P((zval*)p->pDataPtr) & ~IS_CONSTANT_INDEX) >= IS_ARRAY) {
			switch ((Z_TYPE_P((zval*)p->pDataPtr) & ~IS_CONSTANT_INDEX)) {
#endif
				case IS_STRING:
			    case IS_CONSTANT:
					Z_STRVAL_P(ppz) = (char *) interned_estrndup(Z_STRVAL_P((zval*)p->pDataPtr), Z_STRLEN_P((zval*)p->pDataPtr));
					break;
				case IS_ARRAY:
			    case IS_CONSTANT_ARRAY:
					if (((zval*)p->pDataPtr)->value.ht && ((zval*)p->pDataPtr)->value.ht != &EG(symbol_table)) {
						ALLOC_HASHTABLE(ppz->value.ht);
						zend_hash_clone_zval(ppz->value.ht, ((zval*)p->pDataPtr)->value.ht, 0);
					}
					break;
			}
		}

		p = p->pListNext;
	}
	ht->pInternalPointer = ht->pListHead;
}

static void zend_hash_clone_methods(HashTable *ht, HashTable *source, zend_class_entry *old_ce, zend_class_entry *ce TSRMLS_DC)
{
	Bucket *p, *q, **prev;
	ulong nIndex;
	zend_class_entry **new_ce;
	zend_function** new_prototype;
	zend_op_array *new_entry;

	ht->nTableSize = source->nTableSize;
	ht->nTableMask = source->nTableMask;
	ht->nNumOfElements = source->nNumOfElements;
	ht->nNextFreeElement = source->nNextFreeElement;
	ht->pDestructor = ZEND_FUNCTION_DTOR;
#if ZEND_DEBUG
	ht->inconsistent = 0;
#endif
	ht->persistent = 0;
	ht->pListHead = NULL;
	ht->pListTail = NULL;
	ht->pInternalPointer = NULL;
	ht->nApplyCount = 0;
	ht->bApplyProtection = 1;

#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO
	if (!ht->nTableMask) {
		ht->arBuckets = (Bucket**)&uninitialized_bucket;
		return;
	}
#endif

	ht->arBuckets = (Bucket **) ecalloc(ht->nTableSize, sizeof(Bucket *));

	prev = &ht->pListHead;
	p = source->pListHead;
	while (p) {
		nIndex = p->h & ht->nTableMask;

		/* Create bucket and initialize key */
#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO
		if (!p->nKeyLength) {
			q = (Bucket *) emalloc(sizeof(Bucket));
			q->arKey = NULL;
		} else if (IS_INTERNED(p->arKey)) {
			q = (Bucket *) emalloc(sizeof(Bucket));
			q->arKey = p->arKey;
		} else {
			q = (Bucket *) emalloc(sizeof(Bucket) + p->nKeyLength);
			q->arKey = ((char*)q) + sizeof(Bucket);
			memcpy((char*)q->arKey, p->arKey, p->nKeyLength);
		}
#else
		q = (Bucket *) emalloc(sizeof(Bucket) - 1 + p->nKeyLength);
		if (p->nKeyLength) {
			memcpy(q->arKey, p->arKey, p->nKeyLength);
		}
#endif
		q->h = p->h;
		q->nKeyLength = p->nKeyLength;

		/* Insert into hash collision list */
		q->pNext = ht->arBuckets[nIndex];
		q->pLast = NULL;
		if (q->pNext) {
			q->pNext->pLast = q;
		}
		ht->arBuckets[nIndex] = q;

		/* Insert into global list */
		q->pListLast = ht->pListTail;
		ht->pListTail = q;
		q->pListNext = NULL;
		*prev = q;
		prev = &q->pListNext;

		/* Copy data */
		q->pData = (void *) emalloc(sizeof(zend_function));
		new_entry = (zend_op_array*)q->pData;
		*new_entry = *(zend_op_array*)p->pData;
		q->pDataPtr = NULL;

		/* Copy constructor */
		/* we use refcount to show that op_array is referenced from several places */
		if (new_entry->refcount != NULL) {
			accel_xlat_set(p->pData, new_entry);
		}

		zend_prepare_function_for_execution(new_entry);

		if (old_ce == new_entry->scope) {
			new_entry->scope = ce;
		} else {
			if (accel_xlat_get(new_entry->scope, new_ce) == SUCCESS) {
				new_entry->scope = *new_ce;
			} else {
				zend_error(E_ERROR, ACCELERATOR_PRODUCT_NAME " class loading error, class %s, function %s", ce->name, new_entry->function_name);
			}
		}

		/* update prototype */
		if (new_entry->prototype) {
			if (accel_xlat_get(new_entry->prototype, new_prototype) == SUCCESS) {
				new_entry->prototype = *new_prototype;
			} else {
				zend_error(E_ERROR, ACCELERATOR_PRODUCT_NAME " class loading error, class %s, function %s", ce->name, new_entry->function_name);
			}
		}

		p = p->pListNext;
	}
	ht->pInternalPointer = ht->pListHead;
}

static void zend_hash_clone_prop_info(HashTable *ht, HashTable *source, zend_class_entry *old_ce, zend_class_entry *ce TSRMLS_DC)
{
	Bucket *p, *q, **prev;
	ulong nIndex;
	zend_class_entry **new_ce;
	zend_property_info *prop_info;

	ht->nTableSize = source->nTableSize;
	ht->nTableMask = source->nTableMask;
	ht->nNumOfElements = source->nNumOfElements;
	ht->nNextFreeElement = source->nNextFreeElement;
	ht->pDestructor = (dtor_func_t) zend_destroy_property_info;
#if ZEND_DEBUG
	ht->inconsistent = 0;
#endif
	ht->persistent = 0;
	ht->pListHead = NULL;
	ht->pListTail = NULL;
	ht->pInternalPointer = NULL;
	ht->nApplyCount = 0;
	ht->bApplyProtection = 1;

#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO
	if (!ht->nTableMask) {
		ht->arBuckets = (Bucket**)&uninitialized_bucket;
		return;
	}
#endif

	ht->arBuckets = (Bucket **) ecalloc(ht->nTableSize, sizeof(Bucket *));

	prev = &ht->pListHead;
	p = source->pListHead;
	while (p) {
		nIndex = p->h & ht->nTableMask;

		/* Create bucket and initialize key */
#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO
		if (!p->nKeyLength) {
			q = (Bucket *) emalloc(sizeof(Bucket));
			q->arKey = NULL;
		} else if (IS_INTERNED(p->arKey)) {
			q = (Bucket *) emalloc(sizeof(Bucket));
			q->arKey = p->arKey;
		} else {
			q = (Bucket *) emalloc(sizeof(Bucket) + p->nKeyLength);
			q->arKey = ((char*)q) + sizeof(Bucket);
			memcpy((char*)q->arKey, p->arKey, p->nKeyLength);
		}
#else
		q = (Bucket *) emalloc(sizeof(Bucket) - 1 + p->nKeyLength);
		if (p->nKeyLength) {
			memcpy(q->arKey, p->arKey, p->nKeyLength);
		}
#endif
		q->h = p->h;
		q->nKeyLength = p->nKeyLength;

		/* Insert into hash collision list */
		q->pNext = ht->arBuckets[nIndex];
		q->pLast = NULL;
		if (q->pNext) {
			q->pNext->pLast = q;
		}
		ht->arBuckets[nIndex] = q;

		/* Insert into global list */
		q->pListLast = ht->pListTail;
		ht->pListTail = q;
		q->pListNext = NULL;
		*prev = q;
		prev = &q->pListNext;

		/* Copy data */
		q->pData = (void *) emalloc(sizeof(zend_property_info));
		prop_info = q->pData;
		*prop_info = *(zend_property_info*)p->pData;
		q->pDataPtr = NULL;

		/* Copy constructor */
		prop_info->name = interned_estrndup(prop_info->name, prop_info->name_length);
		if (prop_info->doc_comment) {
			if (ZCG(accel_directives).load_comments) {
				prop_info->doc_comment = estrndup(prop_info->doc_comment, prop_info->doc_comment_len);
			} else {
				prop_info->doc_comment = NULL;
			}
		}
		if (prop_info->ce == old_ce) {
			prop_info->ce = ce;
		} else if (accel_xlat_get(prop_info->ce, new_ce) == SUCCESS) {
			prop_info->ce = *new_ce;
		} else {
			zend_error(E_ERROR, ACCELERATOR_PRODUCT_NAME" class loading error, class %s, property %s", ce->name, prop_info->name);
		}

		p = p->pListNext;
	}
	ht->pInternalPointer = ht->pListHead;
}

/* protects reference count, creates copy of statics */
static int zend_prepare_function_for_execution(zend_op_array *op_array)
{
	HashTable *shared_statics = op_array->static_variables;

	/* protect reference count */
	op_array->refcount = &zend_accel_refcount;
	(*op_array->refcount) = ZEND_PROTECTED_REFCOUNT;

	/* copy statics */
	if (shared_statics) {
		ALLOC_HASHTABLE(op_array->static_variables);
		zend_hash_clone_zval(op_array->static_variables, shared_statics, 0);
	}

	return 0;
}

#define zend_update_inherited_handler(handler) \
{ \
	if (ce->handler != NULL) { \
		if (accel_xlat_get(ce->handler, new_func) == SUCCESS) { \
			ce->handler = *new_func; \
		} else { \
			zend_error(E_ERROR, ACCELERATOR_PRODUCT_NAME " class loading error, class %s", ce->name); \
		} \
	} \
}

/* Protects class' refcount, copies default properties, functions and class name */
static void zend_class_copy_ctor(zend_class_entry **pce)
{
	zend_class_entry *ce = *pce;
	zend_class_entry *old_ce = ce;
	zend_class_entry **new_ce;
	zend_function **new_func;
	TSRMLS_FETCH();

	*pce = ce = emalloc(sizeof(zend_class_entry));
	*ce = *old_ce;
	ce->refcount = 1;

	if (old_ce->refcount != 1) {
		/* this class is not used as a parent for any other classes */
		accel_xlat_set(old_ce, ce);
	}

#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO
	if (old_ce->default_properties_table) {
		int i;

		ce->default_properties_table = emalloc(sizeof(zval*) * old_ce->default_properties_count);
		for (i = 0; i < old_ce->default_properties_count; i++) {
			if (old_ce->default_properties_table[i]) {
				ce->default_properties_table[i] = zend_clone_zval(old_ce->default_properties_table[i], 0 TSRMLS_CC);
			} else {
				ce->default_properties_table[i] = NULL;
			}
		}
	}
#else
	zend_hash_clone_zval(&ce->default_properties, &old_ce->default_properties, 0);
#endif

	zend_hash_clone_methods(&ce->function_table, &old_ce->function_table, old_ce, ce TSRMLS_CC);

	/* static members */
#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO
	if (old_ce->default_static_members_table) {
		int i;

		ce->default_static_members_table = emalloc(sizeof(zval*) * old_ce->default_static_members_count);
		for (i = 0; i < old_ce->default_static_members_count; i++) {
			if (old_ce->default_static_members_table[i]) {
				ce->default_static_members_table[i] = zend_clone_zval(old_ce->default_static_members_table[i], 1 TSRMLS_CC);
			} else {
				ce->default_static_members_table[i] = NULL;
			}
		}
	}
	ce->static_members_table = ce->default_static_members_table;
#else
	zend_hash_clone_zval(&ce->default_static_members, &old_ce->default_static_members, 1);
	ce->static_members = &ce->default_static_members;
#endif

	/* properties_info */
	zend_hash_clone_prop_info(&ce->properties_info, &old_ce->properties_info, old_ce, ce TSRMLS_CC);

	/* constants table */
	zend_hash_clone_zval(&ce->constants_table, &old_ce->constants_table, 0);

	ce->name = interned_estrndup(ce->name, ce->name_length);

	/* interfaces aren't really implemented, so we create a new table */
	if (ce->num_interfaces) {
		ce->interfaces = emalloc(sizeof(zend_class_entry *) * ce->num_interfaces);
		memset(ce->interfaces, 0, sizeof(zend_class_entry *) * ce->num_interfaces);
	} else {
		ce->interfaces = NULL;
	}
	if (ZEND_CE_DOC_COMMENT(ce)) {
		if (ZCG(accel_directives).load_comments) {
			ZEND_CE_DOC_COMMENT(ce) = estrndup(ZEND_CE_DOC_COMMENT(ce), ZEND_CE_DOC_COMMENT_LEN(ce));
		} else {
			ZEND_CE_DOC_COMMENT(ce) =  NULL;
		}
	}

	if (ce->parent) {
		if (accel_xlat_get(ce->parent, new_ce) == SUCCESS) {
			ce->parent = *new_ce;
		} else {
			zend_error(E_ERROR, ACCELERATOR_PRODUCT_NAME" class loading error, class %s", ce->name);
		}
	}

	zend_update_inherited_handler(constructor);
	zend_update_inherited_handler(destructor);
	zend_update_inherited_handler(clone);
	zend_update_inherited_handler(__get);
	zend_update_inherited_handler(__set);
	zend_update_inherited_handler(__call);
/* 5.1 stuff */
	zend_update_inherited_handler(serialize_func);
	zend_update_inherited_handler(unserialize_func);
	zend_update_inherited_handler(__isset);
	zend_update_inherited_handler(__unset);
/* 5.2 stuff */
	zend_update_inherited_handler(__tostring);

#if ZEND_EXTENSION_API_NO >= PHP_5_3_X_API_NO
/* 5.3 stuff */
	zend_update_inherited_handler(__callstatic);
#endif

#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO
/* 5.4 traits */
	if (ce->trait_aliases) {
		zend_trait_alias **trait_aliases;
		int i = 0;

		while (ce->trait_aliases[i]) {
			i++;
		}
		trait_aliases = emalloc(sizeof(zend_trait_alias*) * (i + 1));
		i = 0;
		while (ce->trait_aliases[i]) {
			trait_aliases[i] = emalloc(sizeof(zend_trait_alias));
			memcpy(trait_aliases[i], ce->trait_aliases[i], sizeof(zend_trait_alias));
			trait_aliases[i]->trait_method = emalloc(sizeof(zend_trait_method_reference));
			memcpy(trait_aliases[i]->trait_method, ce->trait_aliases[i]->trait_method, sizeof(zend_trait_method_reference));
			if (trait_aliases[i]->trait_method) {
				if (trait_aliases[i]->trait_method->method_name) {
					trait_aliases[i]->trait_method->method_name =
						estrndup(trait_aliases[i]->trait_method->method_name,
							trait_aliases[i]->trait_method->mname_len);
				}
				if (trait_aliases[i]->trait_method->class_name) {
					trait_aliases[i]->trait_method->class_name =
						estrndup(trait_aliases[i]->trait_method->class_name,
							trait_aliases[i]->trait_method->cname_len);
				}
			}

			if (trait_aliases[i]->alias) {
				trait_aliases[i]->alias =
					estrndup(trait_aliases[i]->alias,
						trait_aliases[i]->alias_len);
			}
			i++;
		}
		trait_aliases[i] = NULL;
		ce->trait_aliases = trait_aliases;
	}

	if (ce->trait_precedences) {
		zend_trait_precedence **trait_precedences;
		int i = 0;

		while (ce->trait_precedences[i]) {
			i++;
		}
		trait_precedences = emalloc(sizeof(zend_trait_precedence*) * (i + 1));
		i = 0;
		while (ce->trait_precedences[i]) {
			trait_precedences[i] = emalloc(sizeof(zend_trait_precedence));
			memcpy(trait_precedences[i], ce->trait_precedences[i], sizeof(zend_trait_precedence));
			trait_precedences[i]->trait_method = emalloc(sizeof(zend_trait_method_reference));
			memcpy(trait_precedences[i]->trait_method, ce->trait_precedences[i]->trait_method, sizeof(zend_trait_method_reference));

			trait_precedences[i]->trait_method->method_name =
				estrndup(trait_precedences[i]->trait_method->method_name,
					trait_precedences[i]->trait_method->mname_len);
			trait_precedences[i]->trait_method->class_name =
				estrndup(trait_precedences[i]->trait_method->class_name,
					trait_precedences[i]->trait_method->cname_len);

			if (trait_precedences[i]->exclude_from_classes) {
				zend_class_entry **exclude_from_classes;
				int j = 0;

				while (trait_precedences[i]->exclude_from_classes[j]) {
					j++;
				}
				exclude_from_classes = emalloc(sizeof(zend_class_entry*) * (j + 1));
				j = 0;
				while (trait_precedences[i]->exclude_from_classes[j]) {
					exclude_from_classes[j] = (zend_class_entry*)estrndup(
						(char*)trait_precedences[i]->exclude_from_classes[j],
						strlen((char*)trait_precedences[i]->exclude_from_classes[j]));
					j++;
				}
				exclude_from_classes[j] = NULL;
				trait_precedences[i]->exclude_from_classes = exclude_from_classes;
			}
			i++;
		}
		trait_precedences[i] = NULL;
		ce->trait_precedences = trait_precedences;
	}
#endif
}

static int zend_hash_unique_copy(HashTable *target, HashTable *source, unique_copy_ctor_func_t pCopyConstructor, uint size, int ignore_dups, void **fail_data, void **conflict_data)
{
	Bucket *p;
	void *t;

	p = source->pListHead;
	while (p) {
		if (p->nKeyLength > 0) {
			if (zend_hash_quick_add(target, p->arKey, p->nKeyLength, p->h, p->pData, size, &t) == SUCCESS) {
				if (pCopyConstructor) {
					pCopyConstructor(t);
				}
			} else {
				if (p->nKeyLength > 0 && p->arKey[0] == 0) {
					/* Mangled key */
#if ZEND_EXTENSION_API_NO >= PHP_5_3_X_API_NO
					if (zend_hash_quick_update(target, p->arKey, p->nKeyLength, p->h, p->pData, size, &t) == SUCCESS) {
						if (pCopyConstructor) {
							pCopyConstructor(t);
						}
					}
#endif
				} else if (!ignore_dups && zend_hash_quick_find(target, p->arKey, p->nKeyLength, p->h, &t) == SUCCESS) {
					*fail_data = p->pData;
					*conflict_data = t;
					return FAILURE;
				}
			}
		} else {
			if (!zend_hash_index_exists(target, p->h) && zend_hash_index_update(target, p->h, p->pData, size, &t) == SUCCESS) {
				if (pCopyConstructor) {
					pCopyConstructor(t);
				}
			} else if (!ignore_dups && zend_hash_index_find(target,p->h, &t) == SUCCESS) {
				*fail_data = p->pData;
				*conflict_data = t;
				return FAILURE;
			}
		}
		p = p->pListNext;
	}
	target->pInternalPointer = target->pListHead;

	return SUCCESS;
}

static void zend_accel_function_hash_copy(HashTable *target, HashTable *source, unique_copy_ctor_func_t pCopyConstructor)
{
	zend_function *function1, *function2;
	TSRMLS_FETCH();

	if (zend_hash_unique_copy(target, source, pCopyConstructor, sizeof(zend_function), 0, (void**)&function1, (void**)&function2) != SUCCESS) {
		CG(in_compilation) = 1;
		zend_set_compiled_filename(function1->op_array.filename TSRMLS_CC);
		CG(zend_lineno) = function1->op_array.opcodes[0].lineno;
		if (function2->type == ZEND_USER_FUNCTION
			&& function2->op_array.last > 0) {
			zend_error(E_ERROR, "Cannot redeclare %s() (previously declared in %s:%d)",
					   function1->common.function_name,
					   function2->op_array.filename,
					   (int)function2->op_array.opcodes[0].lineno);
		} else {
			zend_error(E_ERROR, "Cannot redeclare %s()", function1->common.function_name);
		}
	}
}

static void zend_accel_class_hash_copy(HashTable *target, HashTable *source, unique_copy_ctor_func_t pCopyConstructor TSRMLS_DC)
{
	zend_class_entry **pce1, **pce2;

	if (zend_hash_unique_copy(target, source, pCopyConstructor, sizeof(zend_class_entry*), ZCG(accel_directives).ignore_dups, (void**)&pce1, (void**)&pce2) != SUCCESS) {
		CG(in_compilation) = 1;
#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO
		zend_set_compiled_filename((*pce1)->info.user.filename TSRMLS_CC);
		CG(zend_lineno) = (*pce1)->info.user.line_start;
#else
		zend_set_compiled_filename((*pce1)->filename TSRMLS_CC);
		CG(zend_lineno) = (*pce1)->line_start;
#endif
		zend_error(E_ERROR, "Cannot redeclare class %s", (*pce1)->name);
	}
}

#if ZEND_EXTENSION_API_NO < PHP_5_3_X_API_NO
static void zend_do_delayed_early_binding(zend_op_array *op_array, zend_uint early_binding TSRMLS_DC)
{
	zend_uint opline_num = early_binding;

	if ((int)opline_num != -1) {
		zend_bool orig_in_compilation = CG(in_compilation);
		char *orig_compiled_filename = zend_set_compiled_filename(op_array->filename TSRMLS_CC);
		zend_class_entry **pce;

		CG(in_compilation) = 1;
		while ((int)opline_num != -1) {
			if (zend_lookup_class(Z_STRVAL(op_array->opcodes[opline_num - 1].op2.u.constant), Z_STRLEN(op_array->opcodes[opline_num - 1].op2.u.constant), &pce TSRMLS_CC) == SUCCESS) {
				do_bind_inherited_class(&op_array->opcodes[opline_num], EG(class_table), *pce, 1 TSRMLS_CC);
			}
			opline_num = op_array->opcodes[opline_num].result.u.opline_num;
		}
		zend_restore_compiled_filename(orig_compiled_filename TSRMLS_CC);
		CG(in_compilation) = orig_in_compilation;
	}
}
#endif

zend_op_array* zend_accel_load_script(zend_persistent_script *persistent_script, int from_shared_memory TSRMLS_DC)
{
	zend_op_array *op_array;

	op_array = (zend_op_array *) emalloc(sizeof(zend_op_array));
	*op_array = persistent_script->main_op_array;

	if (from_shared_memory) {
		/* Copy all the necessary stuff from shared memory to regular memory, and protect the shared script */
		if (zend_hash_num_elements(&persistent_script->class_table) > 0) {
			zend_hash_init(&ZCG(bind_hash), 10, NULL, NULL, 0);
			zend_accel_class_hash_copy(CG(class_table), &persistent_script->class_table, (unique_copy_ctor_func_t) zend_class_copy_ctor TSRMLS_CC);
			zend_hash_destroy(&ZCG(bind_hash));
		}
		/* we must first to copy all classes and then prepare functions, since functions may try to bind
		   classes - which depend on pre-bind class entries existent in the class table */
		if (zend_hash_num_elements(&persistent_script->function_table) > 0) {
			zend_accel_function_hash_copy(CG(function_table), &persistent_script->function_table, (unique_copy_ctor_func_t)zend_prepare_function_for_execution);
		}

		zend_prepare_function_for_execution(op_array);

		/* Register __COMPILER_HALT_OFFSET__ constant */
		if (persistent_script->compiler_halt_offset != 0 &&
		    persistent_script->full_path) {
			char *name, *cfilename;
			char haltoff[] = "__COMPILER_HALT_OFFSET__";
			int len, clen;

			cfilename = persistent_script->full_path;
			clen = strlen(cfilename);
			zend_mangle_property_name(&name, &len, haltoff, sizeof(haltoff) - 1, cfilename, clen, 0);
			if (!zend_hash_exists(EG(zend_constants), name, len + 1)) {
				zend_register_long_constant(name, len + 1, persistent_script->compiler_halt_offset, CONST_CS, 0 TSRMLS_CC);
			}
			efree(name);
		}

#if ZEND_EXTENSION_API_NO < PHP_5_3_X_API_NO
		if ((int)persistent_script->early_binding != -1) {
			zend_do_delayed_early_binding(op_array, persistent_script->early_binding TSRMLS_CC);
		}
#endif

	} else /* if (!from_shared_memory) */ {
		if (zend_hash_num_elements(&persistent_script->function_table) > 0) {
			zend_accel_function_hash_copy(CG(function_table), &persistent_script->function_table, NULL);
		}
		if (zend_hash_num_elements(&persistent_script->class_table) > 0) {
			zend_accel_class_hash_copy(CG(class_table), &persistent_script->class_table, NULL TSRMLS_CC);
		}
	}

#if ZEND_EXTENSION_API_NO >= PHP_5_3_X_API_NO
	if (op_array->early_binding != (zend_uint)-1) {
		char *orig_compiled_filename = CG(compiled_filename);
		CG(compiled_filename) = persistent_script->full_path;
		zend_do_delayed_early_binding(op_array TSRMLS_CC);
		CG(compiled_filename) = orig_compiled_filename;
	}
#endif

	if (!from_shared_memory) {
		free_persistent_script(persistent_script, 0); /* free only hashes */
	}

	return op_array;
}

/*
 * zend_adler32() is based on zlib implementation
 * Computes the Adler-32 checksum of a data stream
 *
 * Copyright (C) 1995-2005 <NAME>
 * For conditions of distribution and use, see copyright notice in zlib.h
 *
 * Copyright (C) 1995-2005 <NAME> and <NAME>
 *
 *  This software is provided 'as-is', without any express or implied
 *  warranty.  In no event will the authors be held liable for any damages
 *  arising from the use of this software.
 *
 *  Permission is granted to anyone to use this software for any purpose,
 *  including commercial applications, and to alter it and redistribute it
 *  freely, subject to the following restrictions:
 *
 *  1. The origin of this software must not be misrepresented; you must not
 *     claim that you wrote the original software. If you use this software
 *     in a product, an acknowledgment in the product documentation would be
 *     appreciated but is not required.
 *  2. Altered source versions must be plainly marked as such, and must not be
 *     misrepresented as being the original software.
 *  3. This notice may not be removed or altered from any source distribution.
 *
 */

#define ADLER32_BASE 65521 /* largest prime smaller than 65536 */
#define ADLER32_NMAX 5552
/* NMAX is the largest n such that 255n(n+1)/2 + (n+1)(BASE-1) <= 2^32-1 */

#define ADLER32_DO1(buf)        {s1 += *(buf); s2 += s1;}
#define ADLER32_DO2(buf, i)     ADLER32_DO1(buf + i); ADLER32_DO1(buf + i + 1);
#define ADLER32_DO4(buf, i)     ADLER32_DO2(buf, i); ADLER32_DO2(buf, i + 2);
#define ADLER32_DO8(buf, i)     ADLER32_DO4(buf, i); ADLER32_DO4(buf, i + 4);
#define ADLER32_DO16(buf)       ADLER32_DO8(buf, 0); ADLER32_DO8(buf, 8);

unsigned int zend_adler32(unsigned int checksum, signed char *buf, uint len)
{
	unsigned int s1 = checksum & 0xffff;
	unsigned int s2 = (checksum >> 16) & 0xffff;
	signed char *end;

	while (len >= ADLER32_NMAX) {
		len -= ADLER32_NMAX;
		end = buf + ADLER32_NMAX;
		do {
			ADLER32_DO16(buf);
			buf += 16;
		} while (buf != end);
		s1 %= ADLER32_BASE;
		s2 %= ADLER32_BASE;
	}

	if (len) {
		if (len >= 16) {
			end = buf + (len & 0xfff0);
			len &= 0xf;
			do {
				ADLER32_DO16(buf);
				buf += 16;
			} while (buf != end);
		}
		if (len) {
			end = buf + len;
			do {
				ADLER32_DO1(buf);
				buf++;
			} while (buf != end);
		}
		s1 %= ADLER32_BASE;
		s2 %= ADLER32_BASE;
	}

	return (s2 << 16) | s1;
}
