text: /*
   * pg_text_decoder.c - PG::TextDecoder module
   * $Id$
   *
   */
  
  /*
   *
   * Type casts for decoding PostgreSQL string representations to Ruby objects.
   *
   * Decoder classes are defined with pg_define_coder(). This creates a new coder class and
   * assigns a decoder function.
   *
   * Signature of all type cast decoders is:
   *    VALUE decoder_function(t_pg_coder *this, const char *val, int len, int tuple, int field, int enc_idx)
   *
   * Params:
   *   this     - The data part of the coder object that belongs to the decoder function.
   *   val, len - The text or binary data to decode.
   *              The caller ensures, that text data (format=0) is zero terminated so that val[len]=0.
   *              The memory should be used read-only by the callee.
   *   tuple    - Row of the value within the result set.
   *   field    - Column of the value within the result set.
   *   enc_idx  - Index of the Encoding that any output String should get assigned.
   *
   * Returns:
   *   The type casted Ruby object.
   *
   */
  
  #include "ruby/version.h"
  #include "pg.h"
  #include "pg_util.h"
  #ifdef HAVE_INTTYPES_H
  #include <inttypes.h>
  #endif
  #include <ctype.h>
  #include <time.h>
  #if !defined(_WIN32)
  #include <arpa/inet.h>
  #include <sys/socket.h>
  #endif
  #include <string.h>
  
  VALUE rb_mPG_TextDecoder;
  static ID s_id_decode;
  static ID s_id_Rational;
  static ID s_id_new;
  static ID s_id_utc;
  static ID s_id_getlocal;
  static ID s_id_BigDecimal;
  
  static VALUE s_IPAddr;
  static VALUE s_vmasks4;
  static VALUE s_vmasks6;
  static VALUE s_nan, s_pos_inf, s_neg_inf;
  static int use_ipaddr_alloc;
  static ID s_id_lshift;
  static ID s_id_add;
  static ID s_id_mask;
  static ID s_ivar_family;
  static ID s_ivar_addr;
  static ID s_ivar_mask_addr;
  
  /*
   * Document-class: PG::TextDecoder::Boolean < PG::SimpleDecoder
   *
   * This is a decoder class for conversion of PostgreSQL boolean type
   * to Ruby true or false values.
   *
   */
  static VALUE
  pg_text_dec_boolean(t_pg_coder *conv, const char *val, int len, int tuple, int field, int enc_idx)
  {
  	if (len < 1) {
  		rb_raise( rb_eTypeError, "wrong data for text boolean converter in tuple %d field %d", tuple, field);
  	}
  	return *val == 't' ? Qtrue : Qfalse;
  }
  
  /*
   * Document-class: PG::TextDecoder::String < PG::SimpleDecoder
   *
   * This is a decoder class for conversion of PostgreSQL text output to
   * to Ruby String object. The output value will have the character encoding
   * set with PG::Connection#internal_encoding= .
   *
   */
  VALUE
  pg_text_dec_string(t_pg_coder *conv, const char *val, int len, int tuple, int field, int enc_idx)
  {
  	VALUE ret = rb_str_new( val, len );
  	PG_ENCODING_SET_NOCHECK( ret, enc_idx );
  	return ret;
  }
  
  /*
   * Document-class: PG::TextDecoder::Integer < PG::SimpleDecoder
   *
   * This is a decoder class for conversion of PostgreSQL integer types
   * to Ruby Integer objects.
   *
   */
  static VALUE
  pg_text_dec_integer(t_pg_coder *conv, const char *val, int len, int tuple, int field, int enc_idx)
  {
  	long i;
  	int max_len;
  
  	if( sizeof(i) >= 8 && FIXNUM_MAX >= 1000000000000000000LL ){
  		/* 64 bit system can safely handle all numbers up to 18 digits as Fixnum */
  		max_len = 18;
  	} else if( sizeof(i) >= 4 && FIXNUM_MAX >= 1000000000LL ){
  		/* 32 bit system can safely handle all numbers up to 9 digits as Fixnum */
  		max_len = 9;
  	} else {
  		/* unknown -> don't use fast path for int conversion */
  		max_len = 0;
  	}
  
  	if( len <= max_len ){
  		/* rb_cstr2inum() seems to be slow, so we do the int conversion by hand.
  		 * This proved to be 40% faster by the following benchmark:
  		 *
  		 *   conn.type_mapping_for_results = PG::BasicTypeMapForResults.new conn
  		 *   Benchmark.measure do
  		 *     conn.exec("select generate_series(1,1000000)").values }
  		 *   end
  		 */
  		const char *val_pos = val;
  		char digit = *val_pos;
  		int neg;
  		int error = 0;
  
  		if( digit=='-' ){
  			neg = 1;
  			i = 0;
  		}else if( digit>='0' && digit<='9' ){
  			neg = 0;
  			i = digit - '0';
  		} else {
  			error = 1;
  		}
  
  		while (!error && (digit=*++val_pos)) {
  			if( digit>='0' && digit<='9' ){
  				i = i * 10 + (digit - '0');
  			} else {
  				error = 1;
  			}
  		}
  
  		if( !error ){
  			return LONG2FIX(neg ? -i : i);
  		}
  	}
  	/* Fallback to ruby method if number too big or unrecognized. */
  	return rb_cstr2inum(val, 10);
  }
  
  /*
   * Document-class: PG::TextDecoder::Numeric < PG::SimpleDecoder
   *
   * This is a decoder class for conversion of PostgreSQL numeric types
   * to Ruby BigDecimal objects.
   *
   */
  static VALUE
  pg_text_dec_numeric(t_pg_coder *conv, const char *val, int len, int tuple, int field, int enc_idx)
  {
  	return rb_funcall(rb_cObject, s_id_BigDecimal, 1, rb_str_new(val, len));
  }
  
  /*
   * Document-class: PG::TextDecoder::Float < PG::SimpleDecoder
   *
   * This is a decoder class for conversion of PostgreSQL float4 and float8 types
   * to Ruby Float objects.
   *
   */
  static VALUE
  pg_text_dec_float(t_pg_coder *conv, const char *val, int len, int tuple, int field, int enc_idx)
  {
  	switch(*val) {
  		case 'N':
  			return s_nan;
  		case 'I':
  			return s_pos_inf;
  		case '-':
  			if (val[1] == 'I') {
  				return s_neg_inf;
  			} else {
  				return rb_float_new(rb_cstr_to_dbl(val, Qfalse));
  			}
  		default:
  			return rb_float_new(rb_cstr_to_dbl(val, Qfalse));
  	}
  }
  
  struct pg_blob_initialization {
  	char *blob_string;
  	size_t length;
  };
  
  static VALUE pg_create_blob(VALUE v) {
  	struct pg_blob_initialization *bi = (struct pg_blob_initialization *)v;
  	return rb_str_new(bi->blob_string, bi->length);
  }
  
  static VALUE pg_pq_freemem(VALUE mem) {
    PQfreemem((void *)mem);
    return Qfalse;
  }
  
  /*
   * Document-class: PG::TextDecoder::Bytea < PG::SimpleDecoder
   *
   * This is a decoder class for conversion of PostgreSQL bytea type
   * to binary String objects.
   *
   */
  static VALUE
  pg_text_dec_bytea(t_pg_coder *conv, const char *val, int len, int tuple, int field, int enc_idx)
  {
  	struct pg_blob_initialization bi;
  
  	bi.blob_string = (char *)PQunescapeBytea((unsigned char*)val, &bi.length);
  	if (bi.blob_string == NULL) {
  		rb_raise(rb_eNoMemError, "PQunescapeBytea failure: probably not enough memory");
  	}
  	return rb_ensure(pg_create_blob, (VALUE)&bi, pg_pq_freemem, (VALUE)bi.blob_string);
  }
  
  /*
   * array_isspace() --- a non-locale-dependent isspace()
   *
   * We used to use isspace() for parsing array values, but that has
   * undesirable results: an array value might be silently interpreted
   * differently depending on the locale setting.  Now we just hard-wire
   * the traditional ASCII definition of isspace().
   */
  static int
  array_isspace(char ch)
  {
  	if (ch == ' ' ||
  		ch == '\t' ||
  		ch == '\n' ||
  		ch == '\r' ||
  		ch == '\v' ||
  		ch == '\f')
  		return 1;
  	return 0;
  }
  
  static int
  array_isdim(char ch)
  {
  	if ( (ch >= '0' && ch <= '9') ||
  		(ch == '-') ||
  		(ch == '+') ||
  		(ch == ':') )
  		return 1;
  	return 0;
  }
  
  static void
  array_parser_error(t_pg_composite_coder *this, const char *text){
  	if( (this->comp.flags & PG_CODER_FORMAT_ERROR_MASK) == PG_CODER_FORMAT_ERROR_TO_RAISE ){
  		rb_raise( rb_eTypeError, "%s", text );
  	}
  }
  
  /*
   * Array parser functions are thankfully borrowed from here:
   * https://github.com/dockyard/pg_array_parser
   */
  static VALUE
  read_array_without_dim(t_pg_composite_coder *this, int *index, const char *c_pg_array_string, int array_string_length, char *word, int enc_idx, int tuple, int field, t_pg_coder_dec_func dec_func)
  {
  	/* Return value: array */
  	VALUE array;
  	int word_index = 0;
  
  	/* The current character in the input string. */
  	char c;
  
  	/*  0: Currently outside a quoted string, current word never quoted
  	*  1: Currently inside a quoted string
  	* -1: Currently outside a quoted string, current word previously quoted */
  	int openQuote = 0;
  
  	/* Inside quoted input means the next character should be treated literally,
  	* instead of being treated as a metacharacter.
  	* Outside of quoted input, means that the word shouldn't be pushed to the array,
  	* used when the last entry was a subarray (which adds to the array itself). */
  	int escapeNext = 0;
  
  	array = rb_ary_new();
  
  	/* Special case the empty array, so it doesn't need to be handled manually inside
  	* the loop. */
  	if(((*index) < array_string_length) && c_pg_array_string[*index] == '}')
  	{
  		return array;
  	}
  
  	for(;(*index) < array_string_length; ++(*index))
  	{
  		c = c_pg_array_string[*index];
  		if(openQuote < 1)
  		{
  			if(c == this->delimiter || c == '}')
  			{
  				if(!escapeNext)
  				{
  					if(openQuote == 0 && word_index == 4 && !strncmp(word, "NULL", word_index))
  					{
  						rb_ary_push(array, Qnil);
  					}
  					else
  					{
  						VALUE val;
  						word[word_index] = 0;
  						val = dec_func(this->elem, word, word_index, tuple, field, enc_idx);
  						rb_ary_push(array, val);
  					}
  				}
  				if(c == '}')
  				{
  					return array;
  				}
  				escapeNext = 0;
  				openQuote = 0;
  				word_index = 0;
  			}
  			else if(c == '"')
  			{
  				openQuote = 1;
  			}
  			else if(c == '{')
  			{
  				VALUE subarray;
  				(*index)++;
  				subarray = read_array_without_dim(this, index, c_pg_array_string, array_string_length, word, enc_idx, tuple, field, dec_func);
  				rb_ary_push(array, subarray);
  				escapeNext = 1;
  			}
  			else if(c == 0)
  			{
  				array_parser_error( this, "premature end of the array string" );
  				return array;
  			}
  			else
  			{
  				word[word_index] = c;
  				word_index++;
  			}
  		}
  		else if (escapeNext) {
  			word[word_index] = c;
  			word_index++;
  			escapeNext = 0;
  		}
  		else if (c == '\\')
  		{
  			escapeNext = 1;
  		}
  		else if (c == '"')
  		{
  			openQuote = -1;
  		}
  		else
  		{
  			word[word_index] = c;
  			word_index++;
  		}
  	}
  
  	array_parser_error( this, "premature end of the array string" );
  	return array;
  }
  
  /*
   * Document-class: PG::TextDecoder::Array < PG::CompositeDecoder
   *
   * This is a decoder class for PostgreSQL array types.
   *
   * It returns an Array with possibly an arbitrary number of sub-Arrays.
   * All values are decoded according to the #elements_type accessor.
   * Sub-arrays are decoded recursively.
   *
   * This decoder simply ignores any dimension decorations preceding the array values.
   * It returns all array values as regular ruby Array with a zero based index, regardless of the index given in the dimension decoration.
   *
   * An array decoder which respects dimension decorations is waiting to be implemented.
   *
   */
  static VALUE
  pg_text_dec_array(t_pg_coder *conv, const char *c_pg_array_string, int array_string_length, int tuple, int field, int enc_idx)
  {
  	int index = 0;
  	int ndim = 0;
  	VALUE ret;
  	t_pg_composite_coder *this = (t_pg_composite_coder *)conv;
  
  	/*
  	 * If the input string starts with dimension info, read and use that.
  	 * Otherwise, we require the input to be in curly-brace style, and we
  	 * prescan the input to determine dimensions.
  	 *
  	 * Dimension info takes the form of one or more [n] or [m:n] items. The
  	 * outer loop iterates once per dimension item.
  	 */
  	for (;;)
  	{
  		/*
  		 * Note: we currently allow whitespace between, but not within,
  		 * dimension items.
  		 */
  		while (array_isspace(c_pg_array_string[index]))
  			index++;
  		if (c_pg_array_string[index] != '[')
  			break;				/* no more dimension items */
  		index++;
  
  		while (array_isdim(c_pg_array_string[index]))
  			index++;
  
  		if (c_pg_array_string[index] != ']'){
  			array_parser_error( this, "missing \"]\" in array dimensions");
  			break;
  		}
  		index++;
  
  		ndim++;
  	}
  
  	if (ndim == 0)
  	{
  		/* No array dimensions */
  	}
  	else
  	{
  		/* If array dimensions are given, expect '=' operator */
  		if (c_pg_array_string[index] != '=') {
  			array_parser_error( this, "missing assignment operator");
  			index-=2; /* jump back to before "]" so that we don't break behavior to pg < 1.1 */
  		}
  		index++;
  
  		while (array_isspace(c_pg_array_string[index]))
  			index++;
  	}
  
  	if (c_pg_array_string[index] != '{')
  		array_parser_error( this, "array value must start with \"{\" or dimension information");
  	index++;
  
  	if ( index < array_string_length && c_pg_array_string[index] == '}' ) {
  		/* avoid buffer allocation for empty array */
  		ret = rb_ary_new();
  	} else {
  		t_pg_coder_dec_func dec_func = pg_coder_dec_func(this->elem, 0);
  		/* create a buffer of the same length, as that will be the worst case */
  		VALUE buf = rb_str_new(NULL, array_string_length);
  		char *word = RSTRING_PTR(buf);
  
  		ret = read_array_without_dim(this, &index, c_pg_array_string, array_string_length, word, enc_idx, tuple, field, dec_func);
  
  		RB_GC_GUARD(buf);
  	}
  
  	if (c_pg_array_string[index] != '}' )
  		array_parser_error( this, "array value must end with \"}\"");
  	index++;
  
  	/* only whitespace is allowed after the closing brace */
  	for(;index < array_string_length; ++index)
  	{
  		if (!array_isspace(c_pg_array_string[index]))
  			array_parser_error( this, "malformed array literal: Junk after closing right brace.");
  	}
  
  	return ret;
  }
  
  /*
   * Document-class: PG::TextDecoder::Identifier < PG::SimpleDecoder
   *
   * This is the decoder class for PostgreSQL identifiers.
   *
   * Returns an Array of identifiers:
   *   PG::TextDecoder::Identifier.new.decode('schema."table"."column"')
   *      => ["schema", "table", "column"]
   *
   */
  static VALUE
  pg_text_dec_identifier(t_pg_coder *conv, const char *val, int len, int tuple, int field, int enc_idx)
  {
  	/* Return value: array */
  	VALUE array;
  	VALUE elem;
  	int word_index = 0;
  	int index;
  	/* Use a buffer of the same length, as that will be the worst case */
  	PG_VARIABLE_LENGTH_ARRAY(char, word, len + 1, NAMEDATALEN)
  
  	/* The current character in the input string. */
  	char c;
  
  	/*  0: Currently outside a quoted string
  	*  1: Currently inside a quoted string, last char was a quote
  	*  2: Currently inside a quoted string, last char was no quote */
  	int openQuote = 0;
  
  	array = rb_ary_new();
  
  	for(index = 0; index < len; ++index) {
  		c = val[index];
  		if(c == '.' && openQuote < 2 ) {
  			word[word_index] = 0;
  
  			elem = pg_text_dec_string(conv, word, word_index, tuple, field, enc_idx);
  			rb_ary_push(array, elem);
  
  			openQuote = 0;
  			word_index = 0;
  		} else if(c == '"') {
  			if (openQuote == 1) {
  				word[word_index] = c;
  				word_index++;
  				openQuote = 2;
  			} else if (openQuote == 2){
  				openQuote = 1;
  			} else {
  				openQuote = 2;
  			}
  		} else {
  			word[word_index] = c;
  			word_index++;
  		}
  	}
  
  	word[word_index] = 0;
  	elem = pg_text_dec_string(conv, word, word_index, tuple, field, enc_idx);
  	rb_ary_push(array, elem);
  
  	return array;
  }
  
  /*
   * Document-class: PG::TextDecoder::FromBase64 < PG::CompositeDecoder
   *
   * This is a decoder class for conversion of base64 encoded data
   * to it's binary representation. It outputs a binary Ruby String
   * or some other Ruby object, if a #elements_type decoder was defined.
   *
   */
  static VALUE
  pg_text_dec_from_base64(t_pg_coder *conv, const char *val, int len, int tuple, int field, int enc_idx)
  {
  	t_pg_composite_coder *this = (t_pg_composite_coder *)conv;
  	t_pg_coder_dec_func dec_func = pg_coder_dec_func(this->elem, this->comp.format);
  	int decoded_len;
  	/* create a buffer of the expected decoded length */
  	VALUE out_value = rb_str_new(NULL, BASE64_DECODED_SIZE(len));
  
  	decoded_len = base64_decode( RSTRING_PTR(out_value), val, len );
  	rb_str_set_len(out_value, decoded_len);
  
  	/* Is it a pure String conversion? Then we can directly send out_value to the user. */
  	if( this->comp.format == 0 && dec_func == pg_text_dec_string ){
  		PG_ENCODING_SET_NOCHECK( out_value, enc_idx );
  		return out_value;
  	}
  	if( this->comp.format == 1 && dec_func == pg_bin_dec_bytea ){
  		PG_ENCODING_SET_NOCHECK( out_value, rb_ascii8bit_encindex() );
  		return out_value;
  	}
  	out_value = dec_func(this->elem, RSTRING_PTR(out_value), decoded_len, tuple, field, enc_idx);
  
  	return out_value;
  }
  
  static inline int char_to_digit(char c)
  {
  	return c - '0';
  }
  
  static int str2_to_int(const char *str)
  {
  	return char_to_digit(str[0]) * 10
  			+ char_to_digit(str[1]);
  }
  
  static int parse_year(const char **str) {
  	int year = 0;
  	int i;
  	const char * p = *str;
  
  	for(i = 0; isdigit(*p) && i < 7; i++, p++) {
  		year = 10 * year + char_to_digit(*p);
  	}
  
  	*str = p;
  	return year;
  }
  
  #define TZ_NEG 1
  #define TZ_POS 2
  
  /*
   * Document-class: PG::TextDecoder::Timestamp < PG::SimpleDecoder
   *
   * This is a decoder class for conversion of PostgreSQL text timestamps
   * to Ruby Time objects.
   *
   * The following flags can be used to specify time interpretation when no timezone is given:
   * * +PG::Coder::TIMESTAMP_DB_UTC+ : Interpret timestamp as UTC time (default)
   * * +PG::Coder::TIMESTAMP_DB_LOCAL+ : Interpret timestamp as local time
   * * +PG::Coder::TIMESTAMP_APP_UTC+ : Return timestamp as UTC time (default)
   * * +PG::Coder::TIMESTAMP_APP_LOCAL+ : Return timestamp as local time
   *
   * Example:
   *   deco = PG::TextDecoder::Timestamp.new(flags: PG::Coder::TIMESTAMP_DB_UTC | PG::Coder::TIMESTAMP_APP_LOCAL)
   *   deco.decode("2000-01-01 00:00:00")  # => 2000-01-01 01:00:00 +0100
   *   deco.decode("2000-01-01 00:00:00.123-06")  # => 2000-01-01 00:00:00 -0600
   */
  static VALUE pg_text_dec_timestamp(t_pg_coder *conv, const char *val, int len, int tuple, int field, int enc_idx)
  {
  	const char *str = val;
  	int year, mon, day;
  	int hour, min, sec;
  	int nsec = 0;
  	int tz_given = 0;
  	int tz_hour = 0;
  	int tz_min = 0;
  	int tz_sec = 0;
  
  	year = parse_year(&str);
  
  	if ( year > 0
  		&& str[0] == '-' && isdigit(str[1]) && isdigit(str[2])
  		&& str[3] == '-' && isdigit(str[4]) && isdigit(str[5])
  		&& str[6] == ' ' && isdigit(str[7]) && isdigit(str[8])
  		&& str[9] == ':' && isdigit(str[10]) && isdigit(str[11])
  		&& str[12] == ':' && isdigit(str[13]) && isdigit(str[14])
  	) {
  
  		mon = str2_to_int(str+1); str += 3;
  		day = str2_to_int(str+1); str += 3;
  		hour = str2_to_int(str+1); str += 3;
  		min = str2_to_int(str+1); str += 3;
  		sec = str2_to_int(str+1); str += 3;
  
  		if (str[0] == '.' && isdigit(str[1])) {
  			/* nano second part, up to 9 digits */
  			static const int coef[9] = {
  				100000000, 10000000, 1000000,
  				100000, 10000, 1000, 100, 10, 1
  			};
  			int i;
  
  			str++;
  			for (i = 0; i < 9 && isdigit(*str); i++)
  			{
  				nsec += coef[i] * char_to_digit(*str++);
  			}
  			/* consume digits smaller than nsec */
  			while(isdigit(*str)) str++;
  		}
  
  		if ((str[0] == '+' || str[0] == '-') && isdigit(str[1]) && isdigit(str[2])) {
  			tz_given = str[0] == '-' ? TZ_NEG : TZ_POS;
  			tz_hour = str2_to_int(str+1); str += 3;
  
  			if (str[0] == ':' && isdigit(str[1]) && isdigit(str[2]))
  			{
  				tz_min = str2_to_int(str+1); str += 3;
  			}
  			if (str[0] == ':' && isdigit(str[1]) && isdigit(str[2]))
  			{
  				tz_sec = str2_to_int(str+1); str += 3;
  			}
  		}
  
  		if (str[0] == ' ' && str[1] == 'B' && str[2] == 'C') {
  			year = -year + 1;
  			str += 3;
  		}
  
  		if (*str == '\0') { /* must have consumed all the string */
  			VALUE sec_value;
  			VALUE gmt_offset_value;
  			VALUE res;
  
  #if (RUBY_API_VERSION_MAJOR > 2 || (RUBY_API_VERSION_MAJOR == 2 && RUBY_API_VERSION_MINOR >= 3)) && defined(HAVE_TIMEGM)
  			/* Fast path for time conversion */
  			struct tm tm;
  			struct timespec ts;
  			tm.tm_year = year - 1900;
  			tm.tm_mon = mon - 1;
  			tm.tm_mday = day;
  			tm.tm_hour = hour;
  			tm.tm_min = min;
  			tm.tm_sec = sec;
  			tm.tm_isdst = -1;
  
  			if (tz_given) {
  				/* with timezone */
  				time_t time = timegm(&tm);
  				if (time != -1){
  					int gmt_offset;
  
  					gmt_offset = tz_hour * 3600 + tz_min * 60 + tz_sec;
  					if (tz_given == TZ_NEG)
  					{
  						gmt_offset = - gmt_offset;
  					}
  					ts.tv_sec = time - gmt_offset;
  					ts.tv_nsec = nsec;
  					return rb_time_timespec_new(&ts, gmt_offset);
  				}
  			} else {
  				/* without timezone */
  				time_t time;
  
  				if( conv->flags & PG_CODER_TIMESTAMP_DB_LOCAL ) {
  					time = mktime(&tm);
  				} else {
  					time = timegm(&tm);
  				}
  				if (time != -1){
  					ts.tv_sec = time;
  					ts.tv_nsec = nsec;
  					return rb_time_timespec_new(&ts, conv->flags & PG_CODER_TIMESTAMP_APP_LOCAL ? INT_MAX : INT_MAX-1);
  				}
  			}
  			/* Some libc implementations fail to convert certain values,
  			* so that we fall through to the slow path.
  			*/
  #endif
  			if (nsec) {
  				int sec_numerator = sec * 1000000 + nsec / 1000;
  				int sec_denominator = 1000000;
  				sec_value = rb_funcall(Qnil, s_id_Rational, 2,
  						INT2NUM(sec_numerator), INT2NUM(sec_denominator));
  			} else {
  				sec_value = INT2NUM(sec);
  			}
  
  			if (tz_given) {
  				/* with timezone */
  				int gmt_offset;
  
  				gmt_offset = tz_hour * 3600 + tz_min * 60 + tz_sec;
  				if (tz_given == TZ_NEG)
  				{
  					gmt_offset = - gmt_offset;
  				}
  				gmt_offset_value = INT2NUM(gmt_offset);
  			} else {
  				/* without timezone */
  				gmt_offset_value = conv->flags & PG_CODER_TIMESTAMP_DB_LOCAL ? Qnil : INT2NUM(0);
  			}
  
  			res = rb_funcall(rb_cTime, s_id_new, 7,
  					INT2NUM(year),
  					INT2NUM(mon),
  					INT2NUM(day),
  					INT2NUM(hour),
  					INT2NUM(min),
  					sec_value,
  					gmt_offset_value);
  
  			if (tz_given) {
  				/* with timezone */
  				return res;
  			} else {
  				/* without timezone */
  				if( (conv->flags & PG_CODER_TIMESTAMP_DB_LOCAL) && (conv->flags & PG_CODER_TIMESTAMP_APP_LOCAL) ) {
  					return res;
  				} else if( conv->flags & PG_CODER_TIMESTAMP_APP_LOCAL ) {
  					return rb_funcall(res, s_id_getlocal, 0);
  				} else {
  					return rb_funcall(res, s_id_utc, 0);
  				}
  			}
  		}
  	}
  
  	/* fall through to string conversion */
  	return pg_text_dec_string(conv, val, len, tuple, field, enc_idx);
  }
  
  /*
   * Document-class: PG::TextDecoder::Inet < PG::SimpleDecoder
   *
   * This is a decoder class for conversion of PostgreSQL inet type
   * to Ruby IPAddr values.
   *
   */
  static VALUE
  pg_text_dec_inet(t_pg_coder *conv, const char *val, int len, int tuple, int field, int enc_idx)
  {
  	VALUE ip;
  #if defined(_WIN32)
  	ip = rb_str_new(val, len);
  	ip = rb_class_new_instance(1, &ip, s_IPAddr);
  #else
  	VALUE ip_int;
  	VALUE vmasks;
  	char dst[16];
  	char buf[64];
  	int af = strchr(val, '.') ? AF_INET : AF_INET6;
  	int mask = -1;
  
  	if (len >= 64) {
  		rb_raise(rb_eTypeError, "too long data for text inet converter in tuple %d field %d", tuple, field);
  	}
  
  	if (len >= 4) {
  		if (val[len-2] == '/') {
  			mask = val[len-1] - '0';
  			memcpy(buf, val, len-2);
  			buf[len-2] = '\0';
  			val = buf;
  		} else if (val[len-3] == '/') {
  			mask = (val[len-2]- '0')*10 + val[len-1] - '0';
  			memcpy(buf, val, len-3);
  			buf[len-3] = '\0';
  			val = buf;
  		} else if (val[len-4] == '/') {
  			mask = (val[len-3]- '0')*100 + (val[len-2]- '0')*10 + val[len-1] - '0';
  			memcpy(buf, val, len-4);
  			buf[len-4] = '\0';
  			val = buf;
  		}
  	}
  
  	if (1 != inet_pton(af, val, dst)) {
  		rb_raise(rb_eTypeError, "wrong data for text inet converter in tuple %d field %d val", tuple, field);
  	}
  
  	if (af == AF_INET) {
  		unsigned int ip_int_native;
  
  		if (mask == -1) {
  			mask = 32;
  		} else if (mask < 0 || mask > 32) {
  			rb_raise(rb_eTypeError, "invalid mask for IPv4: %d", mask);
  		}
  		vmasks = s_vmasks4;
  
  		ip_int_native = read_nbo32(dst);
  
  		/* Work around broken IPAddr behavior of converting portion
  		   of address after netmask to 0 */
  		switch (mask) {
  			case 0:
  				ip_int_native = 0;
  				break;
  			case 32:
  				/* nothing to do */
  				break;
  			default:
  				ip_int_native &= ~((1UL<<(32-mask))-1);
  				break;
  		}
  
  		ip_int = UINT2NUM(ip_int_native);
  	} else {
  		unsigned long long * dstllp = (unsigned long long *)dst;
  		unsigned long long ip_int_native1;
  		unsigned long long ip_int_native2;
  
  		if (mask == -1) {
  			mask = 128;
  		} else if (mask < 0 || mask > 128) {
  			rb_raise(rb_eTypeError, "invalid mask for IPv6: %d", mask);
  		}
  		vmasks = s_vmasks6;
  
  		ip_int_native1 = read_nbo64(dstllp);
  		dstllp++;
  		ip_int_native2 = read_nbo64(dstllp);
  
  		if (mask == 128) {
  			/* nothing to do */
  		} else if (mask == 64) {
  			ip_int_native2 = 0;
  		} else if (mask == 0) {
  			ip_int_native1 = 0;
  			ip_int_native2 = 0;
  		} else if (mask < 64) {
  			ip_int_native1 &= ~((1ULL<<(64-mask))-1);
  			ip_int_native2 = 0;
  		} else {
  			ip_int_native2 &= ~((1ULL<<(128-mask))-1);
  		}
  
  		/* 4 Bignum allocations */
  		ip_int = ULL2NUM(ip_int_native1);
  		ip_int = rb_funcall(ip_int, s_id_lshift, 1, INT2NUM(64));
  		ip_int = rb_funcall(ip_int, s_id_add, 1, ULL2NUM(ip_int_native2));
  	}
  
  	if (use_ipaddr_alloc) {
  		ip = rb_obj_alloc(s_IPAddr);
  		rb_ivar_set(ip, s_ivar_family, INT2NUM(af));
  		rb_ivar_set(ip, s_ivar_addr, ip_int);
  		rb_ivar_set(ip, s_ivar_mask_addr, RARRAY_AREF(vmasks, mask));
  	} else {
  		VALUE ip_args[2];
  		ip_args[0] = ip_int;
  		ip_args[1] = INT2NUM(af);
  		ip = rb_class_new_instance(2, ip_args, s_IPAddr);
  		ip = rb_funcall(ip, s_id_mask, 1, INT2NUM(mask));
  	}
  
  #endif
  	return ip;
  }
  
  void
  init_pg_text_decoder()
  {
  	rb_require("ipaddr");
  	s_IPAddr = rb_funcall(rb_cObject, rb_intern("const_get"), 1, rb_str_new2("IPAddr"));
  	rb_global_variable(&s_IPAddr);
  	s_ivar_family = rb_intern("@family");
  	s_ivar_addr = rb_intern("@addr");
  	s_ivar_mask_addr = rb_intern("@mask_addr");
  	s_id_lshift = rb_intern("<<");
  	s_id_add = rb_intern("+");
  	s_id_mask = rb_intern("mask");
  
  	use_ipaddr_alloc = RTEST(rb_eval_string("IPAddr.new.instance_variables.sort == [:@addr, :@family, :@mask_addr]"));
  
  	s_vmasks4 = rb_eval_string("a = [0]*33; a[0] = 0; a[32] = 0xffffffff; 31.downto(1){|i| a[i] = a[i+1] - (1 << (31 - i))}; a.freeze");
  	rb_global_variable(&s_vmasks4);
  	s_vmasks6 = rb_eval_string("a = [0]*129; a[0] = 0; a[128] = 0xffffffffffffffffffffffffffffffff; 127.downto(1){|i| a[i] = a[i+1] - (1 << (127 - i))}; a.freeze");
  	rb_global_variable(&s_vmasks6);
  
  	s_id_decode = rb_intern("decode");
  	s_id_Rational = rb_intern("Rational");
  	s_id_new = rb_intern("new");
  	s_id_utc = rb_intern("utc");
  	s_id_getlocal = rb_intern("getlocal");
  
  	rb_require("bigdecimal");
  	s_id_BigDecimal = rb_intern("BigDecimal");
  	s_nan = rb_eval_string("0.0/0.0");
  	rb_global_variable(&s_nan);
  	s_pos_inf = rb_eval_string("1.0/0.0");
  	rb_global_variable(&s_pos_inf);
  	s_neg_inf = rb_eval_string("-1.0/0.0");
  	rb_global_variable(&s_neg_inf);
  
  	/* This module encapsulates all decoder classes with text input format */
  	rb_mPG_TextDecoder = rb_define_module_under( rb_mPG, "TextDecoder" );
  
  	/* Make RDoc aware of the decoder classes... */
  	/* dummy = rb_define_class_under( rb_mPG_TextDecoder, "Boolean", rb_cPG_SimpleDecoder ); */
  	pg_define_coder( "Boolean", pg_text_dec_boolean, rb_cPG_SimpleDecoder, rb_mPG_TextDecoder );
  	/* dummy = rb_define_class_under( rb_mPG_TextDecoder, "Integer", rb_cPG_SimpleDecoder ); */
  	pg_define_coder( "Integer", pg_text_dec_integer, rb_cPG_SimpleDecoder, rb_mPG_TextDecoder );
  	/* dummy = rb_define_class_under( rb_mPG_TextDecoder, "Float", rb_cPG_SimpleDecoder ); */
  	pg_define_coder( "Float", pg_text_dec_float, rb_cPG_SimpleDecoder, rb_mPG_TextDecoder );
  	/* dummy = rb_define_class_under( rb_mPG_TextDecoder, "Numeric", rb_cPG_SimpleDecoder ); */
  	pg_define_coder( "Numeric", pg_text_dec_numeric, rb_cPG_SimpleDecoder, rb_mPG_TextDecoder );
  	/* dummy = rb_define_class_under( rb_mPG_TextDecoder, "String", rb_cPG_SimpleDecoder ); */
  	pg_define_coder( "String", pg_text_dec_string, rb_cPG_SimpleDecoder, rb_mPG_TextDecoder );
  	/* dummy = rb_define_class_under( rb_mPG_TextDecoder, "Bytea", rb_cPG_SimpleDecoder ); */
  	pg_define_coder( "Bytea", pg_text_dec_bytea, rb_cPG_SimpleDecoder, rb_mPG_TextDecoder );
  	/* dummy = rb_define_class_under( rb_mPG_TextDecoder, "Identifier", rb_cPG_SimpleDecoder ); */
  	pg_define_coder( "Identifier", pg_text_dec_identifier, rb_cPG_SimpleDecoder, rb_mPG_TextDecoder );
  	/* dummy = rb_define_class_under( rb_mPG_TextDecoder, "Timestamp", rb_cPG_SimpleDecoder ); */
  	pg_define_coder( "Timestamp", pg_text_dec_timestamp, rb_cPG_SimpleDecoder, rb_mPG_TextDecoder);
  	/* dummy = rb_define_class_under( rb_mPG_TextDecoder, "Inet", rb_cPG_SimpleDecoder ); */
  	pg_define_coder( "Inet", pg_text_dec_inet, rb_cPG_SimpleDecoder, rb_mPG_TextDecoder);
  
  	/* dummy = rb_define_class_under( rb_mPG_TextDecoder, "Array", rb_cPG_CompositeDecoder ); */
  	pg_define_coder( "Array", pg_text_dec_array, rb_cPG_CompositeDecoder, rb_mPG_TextDecoder );
  	/* dummy = rb_define_class_under( rb_mPG_TextDecoder, "FromBase64", rb_cPG_CompositeDecoder ); */
  	pg_define_coder( "FromBase64", pg_text_dec_from_base64, rb_cPG_CompositeDecoder, rb_mPG_TextDecoder );
  }
  