  void testcompressDecompressWithUncommonParams(
      const uint32_t quality, const uint32_t window_bits, const uint32_t input_block_bits,
      const bool disable_literal_context_modeling,
      const Compression::Brotli::Compressor::BrotliCompressorImpl::EncoderMode encoder_mode,
      const bool disable_ring_buffer_reallocation) {
    Buffer::OwnedImpl buffer;
    Buffer::OwnedImpl accumulation_buffer;

    Compression::Brotli::Compressor::BrotliCompressorImpl compressor{
        quality,      window_bits, input_block_bits, disable_literal_context_modeling,
        encoder_mode, 4096};

    std::string original_text{};
    for (uint64_t i = 0; i < 30; ++i) {
      TestUtility::feedBufferWithRandomCharacters(buffer, default_input_size * i, i);
      original_text.append(buffer.toString());
      compressor.compress(buffer, Envoy::Compression::Compressor::State::Flush);
      accumulation_buffer.add(buffer);
      drainBuffer(buffer);
    }
    ASSERT_EQ(0, buffer.length());

    compressor.compress(buffer, Envoy::Compression::Compressor::State::Finish);
    accumulation_buffer.add(buffer);

    drainBuffer(buffer);
    ASSERT_EQ(0, buffer.length());

    Stats::IsolatedStoreImpl stats_store{};
    BrotliDecompressorImpl decompressor{stats_store, "test.", 4096,
                                        disable_ring_buffer_reallocation};

    decompressor.decompress(accumulation_buffer, buffer);
    std::string decompressed_text{buffer.toString()};

    ASSERT_EQ(original_text.length(), decompressed_text.length());
    EXPECT_EQ(original_text, decompressed_text);
    EXPECT_EQ(0, stats_store.counterFromString("test.brotli_error").value());
  }


TEST_F(ZstdDecompressorStatsTest, ChargeErrorStats) {
  EXPECT_FALSE(isError(0)); // no error

  EXPECT_TRUE(isError(ZSTD_ERROR(memory_allocation)));
  ASSERT_EQ(stats_store_.counterFromString("test.zstd_memory_error").value(), 1);

  EXPECT_TRUE(isError(ZSTD_ERROR(dictionary_corrupted)));
  ASSERT_EQ(stats_store_.counterFromString("test.zstd_dictionary_error").value(), 1);
  EXPECT_TRUE(isError(ZSTD_ERROR(dictionary_wrong)));
  ASSERT_EQ(stats_store_.counterFromString("test.zstd_dictionary_error").value(), 2);

  EXPECT_TRUE(isError(ZSTD_ERROR(checksum_wrong)));
  ASSERT_EQ(stats_store_.counterFromString("test.zstd_checksum_wrong_error").value(), 1);

  EXPECT_TRUE(isError(-1)); // generic error
  ASSERT_EQ(stats_store_.counterFromString("test.zstd_generic_error").value(), 1);
}


TEST_F(ZlibDecompressorStatsTest, ChargeErrorStats) {
  decompressor_.init(31);

  chargeErrorStats(Z_ERRNO);
  ASSERT_EQ(stats_store_.counterFromString("test.zlib_errno").value(), 1);
  chargeErrorStats(Z_STREAM_ERROR);
  ASSERT_EQ(stats_store_.counterFromString("test.zlib_stream_error").value(), 1);
  chargeErrorStats(Z_DATA_ERROR);
  ASSERT_EQ(stats_store_.counterFromString("test.zlib_data_error").value(), 1);
  chargeErrorStats(Z_MEM_ERROR);
  ASSERT_EQ(stats_store_.counterFromString("test.zlib_mem_error").value(), 1);
  chargeErrorStats(Z_BUF_ERROR);
  ASSERT_EQ(stats_store_.counterFromString("test.zlib_buf_error").value(), 1);
  chargeErrorStats(Z_VERSION_ERROR);
  ASSERT_EQ(stats_store_.counterFromString("test.zlib_version_error").value(), 1);
}


TEST_F(ZstdDecompressorImplTest, DetectExcessiveCompressionRatio) {
  const absl::string_view ten_whitespaces = "          ";
  Buffer::OwnedImpl buffer;
  for (int i = 0; i < 1000; i++) {
    buffer.add(ten_whitespaces);
  }

  Zstd::Compressor::ZstdCompressorImpl compressor{default_compression_level_,
                                                  default_enable_checksum_, default_strategy_,
                                                  default_cdict_manager_, 4096};
  compressor.compress(buffer, Envoy::Compression::Compressor::State::Finish);

  Buffer::OwnedImpl output_buffer;
  Stats::IsolatedStoreImpl stats_store{};
  ZstdDecompressorImpl decompressor{stats_store, "test.", default_ddict_manager_, 16};
  decompressor.decompress(buffer, output_buffer);
  ASSERT_EQ(stats_store.counterFromString("test.zstd_generic_error").value(), 1);
}


BrotliContext::BrotliContext(uint32_t chunk_size, uint32_t max_output_size)
    : max_output_size_{max_output_size}, chunk_size_{chunk_size},
      chunk_ptr_{std::make_unique<uint8_t[]>(chunk_size)}, next_in_{}, next_out_{chunk_ptr_.get()},
      avail_in_{0}, avail_out_{chunk_size} {}


void LibRaw::kodak_thumb_loader()
{
	INT64 est_datasize = T.theight * T.twidth / 3; // is 0.3 bytes per pixel good estimate?
	if (ID.toffset < 0)
		throw LIBRAW_EXCEPTION_IO_CORRUPT;

	if (ID.toffset + est_datasize > ID.input->size() + THUMB_READ_BEYOND)
		throw LIBRAW_EXCEPTION_IO_EOF;

	// some kodak cameras
  ushort s_height = S.height, s_width = S.width,s_iwidth = S.iwidth,s_iheight=S.iheight;
  ushort s_flags = libraw_internal_data.unpacker_data.load_flags;
  libraw_internal_data.unpacker_data.load_flags = 12;
  int s_colors = P1.colors;
  unsigned s_filters = P1.filters;
  ushort (*s_image)[4] = imgdata.image;

  S.height = T.theight;
  S.width  = T.twidth;
  P1.filters = 0;

  if (thumb_load_raw == &CLASS kodak_ycbcr_load_raw)
    {
      S.height += S.height & 1;
      S.width  += S.width  & 1;
    }

  imgdata.image = (ushort (*)[4]) calloc (S.iheight*S.iwidth, sizeof (*imgdata.image));
  merror (imgdata.image, "LibRaw::kodak_thumb_loader()");

  ID.input->seek(ID.toffset, SEEK_SET);
  // read kodak thumbnail into T.image[]
  try {
	  (this->*thumb_load_raw)();
  } catch (...)
  {
	  free(imgdata.image);
	  imgdata.image  = s_image;

	  T.twidth = 0;
	  S.width = s_width;

	  S.iwidth = s_iwidth;
	  S.iheight = s_iheight;

	  T.theight = 0;
	  S.height = s_height;

	  T.tcolors = 0;
	  P1.colors = s_colors;

	  P1.filters = s_filters;
	  T.tlength=0;
	  libraw_internal_data.unpacker_data.load_flags = s_flags;
	  return;
  }

  // copy-n-paste from image pipe
#define MIN(a,b) ((a) < (b) ? (a) : (b))
#define MAX(a,b) ((a) > (b) ? (a) : (b))
#define LIM(x,min,max) MAX(min,MIN(x,max))
#ifndef CLIP
#define CLIP(x) LIM(x,0,65535)
#endif
#define SWAP(a,b) { a ^= b; a ^= (b ^= a); }

  // from scale_colors
  {
    double   dmax;
    float scale_mul[4];
    int c,val;
    for (dmax=DBL_MAX, c=0; c < 3; c++)
      if (dmax > C.pre_mul[c])
        dmax = C.pre_mul[c];

    for( c=0; c< 3; c++)
      scale_mul[c] = (C.pre_mul[c] / dmax) * 65535.0 / C.maximum;
    scale_mul[3] = scale_mul[1];

    size_t size = S.height * S.width;
    for (unsigned i=0; i < size*4 ; i++)
      {
        val = imgdata.image[0][i];
        if(!val) continue;
        val *= scale_mul[i & 3];
        imgdata.image[0][i] = CLIP(val);
      }
  }

  // from convert_to_rgb
  ushort *img;
  int row,col;

  int  (*t_hist)[LIBRAW_HISTOGRAM_SIZE] =  (int (*)[LIBRAW_HISTOGRAM_SIZE]) calloc(sizeof(*t_hist),4);
  merror (t_hist, "LibRaw::kodak_thumb_loader()");

  float out[3],
    out_cam[3][4] =
    {
      {2.81761312, -1.98369181, 0.166078627, 0},
      {-0.111855984, 1.73688626, -0.625030339, 0},
      {-0.0379119813, -0.891268849, 1.92918086, 0}
    };

  for (img=imgdata.image[0], row=0; row < S.height; row++)
    for (col=0; col < S.width; col++, img+=4)
      {
        out[0] = out[1] = out[2] = 0;
        int c;
        for(c=0;c<3;c++)
          {
            out[0] += out_cam[0][c] * img[c];
            out[1] += out_cam[1][c] * img[c];
            out[2] += out_cam[2][c] * img[c];
          }
        for(c=0; c<3; c++)
          img[c] = CLIP((int) out[c]);
        for(c=0; c<P1.colors;c++)
          t_hist[c][img[c] >> 3]++;

      }

  // from gamma_lut
  int  (*save_hist)[LIBRAW_HISTOGRAM_SIZE] = libraw_internal_data.output_data.histogram;
  libraw_internal_data.output_data.histogram = t_hist;

  // make curve output curve!
  ushort (*t_curve) = (ushort*) calloc(sizeof(C.curve),1);
  merror (t_curve, "LibRaw::kodak_thumb_loader()");
  memmove(t_curve,C.curve,sizeof(C.curve));
  memset(C.curve,0,sizeof(C.curve));
  {
    int perc, val, total, t_white=0x2000,c;

    perc = S.width * S.height * 0.01;		/* 99th percentile white level */
    if (IO.fuji_width) perc /= 2;
    if (!((O.highlight & ~2) || O.no_auto_bright))
      for (t_white=c=0; c < P1.colors; c++) {
        for (val=0x2000, total=0; --val > 32; )
          if ((total += libraw_internal_data.output_data.histogram[c][val]) > perc) break;
        if (t_white < val) t_white = val;
      }
    gamma_curve (O.gamm[0], O.gamm[1], 2, (t_white << 3)/O.bright);
  }

  libraw_internal_data.output_data.histogram = save_hist;
  free(t_hist);

  // from write_ppm_tiff - copy pixels into bitmap

  S.iheight = S.height;
  S.iwidth  = S.width;
  if (S.flip & 4) SWAP(S.height,S.width);

  if(T.thumb) free(T.thumb);
  T.thumb = (char*) calloc (S.width * S.height, P1.colors);
  merror (T.thumb, "LibRaw::kodak_thumb_loader()");
  T.tlength = S.width * S.height * P1.colors;

// from write_tiff_ppm
  {
	  int soff = flip_index(0, 0);
	  int cstep = flip_index(0, 1) - soff;
	  int rstep = flip_index(1, 0) - flip_index(0, S.width);

	  for (int row = 0; row < S.height; row++, soff += rstep)
	  {
		  char *ppm = T.thumb + row*S.width*P1.colors;
		  for (int col = 0; col < S.width; col++, soff += cstep)
			  for (int c = 0; c < P1.colors; c++)
				  ppm[col*P1.colors + c] = imgdata.color.curve[imgdata.image[soff][c]] >> 8;
	  }
  }

  memmove(C.curve, t_curve, sizeof(C.curve));
  free(t_curve);

  // restore variables
  free(imgdata.image);
  imgdata.image = s_image;

  T.twidth = S.width;
  S.width = s_width;

  S.iwidth = s_iwidth;
  S.iheight = s_iheight;

  T.theight = S.height;
  S.height = s_height;

  T.tcolors = P1.colors;
  P1.colors = s_colors;

  P1.filters = s_filters;
  libraw_internal_data.unpacker_data.load_flags = s_flags;
}


void LibRaw::nikon_load_striped_packed_raw()
{
	int vbits=0, bwide, rbits, bite,row, col, val, i;

	UINT64 bitbuf=0;
	unsigned load_flags = 24; //libraw_internal_data.unpacker_data.load_flags;
	unsigned tiff_bps = libraw_internal_data.unpacker_data.tiff_bps;
	int tiff_compress = libraw_internal_data.unpacker_data.tiff_compress;

	struct tiff_ifd_t * ifd = &tiff_ifd[0];
	while (ifd < &tiff_ifd[libraw_internal_data.identify_data.tiff_nifds] && ifd->offset != libraw_internal_data.unpacker_data.data_offset) ++ifd;
	if (ifd == &tiff_ifd[libraw_internal_data.identify_data.tiff_nifds])
		throw LIBRAW_EXCEPTION_DECODE_RAW;

	if(!ifd->rows_per_strip || !ifd->strip_offsets_count)
		return; // not unpacked
	int stripcnt = 0;

	bwide = S.raw_width *  tiff_bps / 8;
	bwide += bwide & load_flags >> 7;
	rbits = bwide * 8 - S.raw_width * tiff_bps;
	if (load_flags & 1) bwide = bwide * 16 / 15;
	bite = 8 + (load_flags & 24);
	for (row=0; row < S.raw_height; row++)
	{
		checkCancel();
		if(!(row%ifd->rows_per_strip))
		{
			if(stripcnt >= ifd->strip_offsets_count)
				return; // run out of data
			libraw_internal_data.internal_data.input->seek(ifd->strip_offsets[stripcnt],SEEK_SET);
			stripcnt++;
		}
		for (col=0; col < S.raw_width; col++)
		{
			for (vbits -= tiff_bps; vbits < 0; vbits += bite)
			{
				bitbuf <<= bite;
				for (i=0; i < bite; i+=8)
					bitbuf |= (unsigned) (libraw_internal_data.internal_data.input->get_char() << i);
			}
			imgdata.rawdata.raw_image[(row)*S.raw_width+(col)] = bitbuf << (64-tiff_bps-vbits) >> (64-tiff_bps);
		}
		vbits -= rbits;
	}
}


void LibRaw::deflate_dng_load_raw()
{
	struct tiff_ifd_t * ifd = &tiff_ifd[0];
	while (ifd < &tiff_ifd[libraw_internal_data.identify_data.tiff_nifds] && ifd->offset != libraw_internal_data.unpacker_data.data_offset) ++ifd;
	if (ifd == &tiff_ifd[libraw_internal_data.identify_data.tiff_nifds])
	{
		throw LIBRAW_EXCEPTION_DECODE_RAW;
	}

	float *float_raw_image=0;
	float max = 0.f;

	if(ifd->samples!=1 && ifd->samples!=3 && ifd->samples !=4)
		throw LIBRAW_EXCEPTION_DECODE_RAW; // Only float deflated supported

	if(libraw_internal_data.unpacker_data.tiff_samples != ifd->samples)
		throw LIBRAW_EXCEPTION_DECODE_RAW; // Wrong IFD

	size_t tilesH = (imgdata.sizes.raw_width + libraw_internal_data.unpacker_data.tile_width - 1) / libraw_internal_data.unpacker_data.tile_width;
	size_t tilesV = (imgdata.sizes.raw_height + libraw_internal_data.unpacker_data.tile_length - 1) / libraw_internal_data.unpacker_data.tile_length;
	size_t tileCnt = tilesH * tilesV;

	if (ifd->sample_format == 3)
	{  // Floating point data
		float_raw_image = (float*)calloc(tileCnt*libraw_internal_data.unpacker_data.tile_length* libraw_internal_data.unpacker_data.tile_width * ifd->samples,sizeof(float));
		//imgdata.color.maximum = 65535;
		//imgdata.color.black = 0;
		//memset(imgdata.color.cblack,0,sizeof(imgdata.color.cblack));
	}
	else
		throw LIBRAW_EXCEPTION_DECODE_RAW; // Only float deflated supported

	int xFactor;
	switch(ifd->predictor)
	{
		case 3:
		default:
			xFactor = 1; break;
		case 34894: xFactor = 2; break;
		case 34895: xFactor = 4; break;
	}

	if (libraw_internal_data.unpacker_data.tile_length < INT_MAX)
	{
		if(tileCnt<1 || tileCnt > 1000000)
			throw LIBRAW_EXCEPTION_DECODE_RAW;

		size_t *tOffsets = (size_t*)malloc(tileCnt*sizeof(size_t));
		for (int t = 0; t < tileCnt; ++t)
			tOffsets[t] = get4();

		size_t *tBytes = (size_t*) malloc(tileCnt*sizeof(size_t));
		unsigned long maxBytesInTile = 0;
		if (tileCnt == 1)
			tBytes[0] = maxBytesInTile = ifd->bytes;
		else
		{
			libraw_internal_data.internal_data.input->seek(ifd->bytes, SEEK_SET);
			for (size_t t = 0; t < tileCnt; ++t)
			{
				tBytes[t] = get4();
				maxBytesInTile = MAX(maxBytesInTile,tBytes[t]);
			}
		}
		unsigned tilePixels = libraw_internal_data.unpacker_data.tile_width * libraw_internal_data.unpacker_data.tile_length;
		unsigned pixelSize = sizeof(float)*ifd->samples;
		unsigned tileBytes = tilePixels*pixelSize;
		unsigned tileRowBytes = libraw_internal_data.unpacker_data.tile_width*pixelSize;

		unsigned char *cBuffer = (unsigned char*)malloc(maxBytesInTile);
		unsigned char *uBuffer = (unsigned char*)malloc(tileBytes+tileRowBytes); // extra row for decoding

		for (size_t y = 0, t = 0; y < imgdata.sizes.raw_height; y += libraw_internal_data.unpacker_data.tile_length)
		{
			for (size_t x = 0; x < imgdata.sizes.raw_width; x += libraw_internal_data.unpacker_data.tile_width, ++t)
			{
				libraw_internal_data.internal_data.input->seek(tOffsets[t], SEEK_SET);
				libraw_internal_data.internal_data.input->read(cBuffer, 1, tBytes[t]);
				unsigned long dstLen = tileBytes;
				int err = uncompress(uBuffer+tileRowBytes, &dstLen, cBuffer, tBytes[t]);
				if (err != Z_OK)
				{
					free(tOffsets);
					free(tBytes);
					free(cBuffer);
					free(uBuffer);
					throw LIBRAW_EXCEPTION_DECODE_RAW;
					return;
				}
				else
				{
					int bytesps = ifd->bps >> 3;
					size_t rowsInTile = y + libraw_internal_data.unpacker_data.tile_length > imgdata.sizes.raw_height ? imgdata.sizes.raw_height - y : libraw_internal_data.unpacker_data.tile_length;
					size_t colsInTile= x + libraw_internal_data.unpacker_data.tile_width > imgdata.sizes.raw_width ? imgdata.sizes.raw_width - x : libraw_internal_data.unpacker_data.tile_width;

					for (size_t row = 0; row < rowsInTile; ++row) // do not process full tile if not needed
					{
						unsigned char* dst = uBuffer + row*libraw_internal_data.unpacker_data.tile_width*bytesps*ifd->samples;
						unsigned char* src = dst+tileRowBytes;
						DecodeFPDelta (src,dst,
							libraw_internal_data.unpacker_data.tile_width/ xFactor,
							ifd->samples * xFactor,
							bytesps);
						float lmax = expandFloats(dst, libraw_internal_data.unpacker_data.tile_width*ifd->samples, bytesps);
						max = MAX(max,lmax);
						unsigned char* dst2 = (unsigned char*)&float_raw_image[((y+row)*imgdata.sizes.raw_width + x)*ifd->samples];
						memmove(dst2,dst,colsInTile*ifd->samples*sizeof(float));
					}
				}
			}
		}
		free(tOffsets);
		free(tBytes);
		free(cBuffer);
		free(uBuffer);
	}
	imgdata.color.fmaximum = max;

	// Set fields according to data format

	imgdata.rawdata.raw_alloc = float_raw_image;
	if(ifd->samples == 1)
	{
		imgdata.rawdata.float_image = float_raw_image;
		imgdata.rawdata.sizes.raw_pitch = imgdata.sizes.raw_pitch = imgdata.sizes.raw_width*4;
	}
	else if(ifd->samples == 3)
	{
		imgdata.rawdata.float3_image = (float(*)[3])float_raw_image;
		imgdata.rawdata.sizes.raw_pitch = imgdata.sizes.raw_pitch = imgdata.sizes.raw_width*12;
	}
	else if(ifd->samples == 4)
	{
		imgdata.rawdata.float4_image = (float(*)[4])float_raw_image;
		imgdata.rawdata.sizes.raw_pitch = imgdata.sizes.raw_pitch = imgdata.sizes.raw_width*16;
	}

	if(imgdata.params.raw_processing_options & LIBRAW_PROCESSING_CONVERTFLOAT_TO_INT)
		convertFloatToInt(); // with default settings
}


static void mark_stack_slot_scratched(struct bpf_verifier_env *env, u32 spi)
{
	env->scratched_stack_slots |= 1UL << spi;
}


static int __check_func_call(struct bpf_verifier_env *env, struct bpf_insn *insn,
			     int *insn_idx, int subprog,
			     set_callee_state_fn set_callee_state_cb)
{
	struct bpf_verifier_state *state = env->cur_state;
	struct bpf_func_info_aux *func_info_aux;
	struct bpf_func_state *caller, *callee;
	int err;
	bool is_global = false;

	if (state->curframe + 1 >= MAX_CALL_FRAMES) {
		verbose(env, "the call stack of %d frames is too deep\n",
			state->curframe + 2);
		return -E2BIG;
	}

	caller = state->frame[state->curframe];
	if (state->frame[state->curframe + 1]) {
		verbose(env, "verifier bug. Frame %d already allocated\n",
			state->curframe + 1);
		return -EFAULT;
	}

	func_info_aux = env->prog->aux->func_info_aux;
	if (func_info_aux)
		is_global = func_info_aux[subprog].linkage == BTF_FUNC_GLOBAL;
	err = btf_check_subprog_arg_match(env, subprog, caller->regs);
	if (err == -EFAULT)
		return err;
	if (is_global) {
		if (err) {
			verbose(env, "Caller passes invalid args into func#%d\n",
				subprog);
			return err;
		} else {
			if (env->log.level & BPF_LOG_LEVEL)
				verbose(env,
					"Func#%d is global and valid. Skipping.\n",
					subprog);
			clear_caller_saved_regs(env, caller->regs);

			/* All global functions return a 64-bit SCALAR_VALUE */
			mark_reg_unknown(env, caller->regs, BPF_REG_0);
			caller->regs[BPF_REG_0].subreg_def = DEF_NOT_SUBREG;

			/* continue with next insn after call */
			return 0;
		}
	}

	if (insn->code == (BPF_JMP | BPF_CALL) &&
	    insn->imm == BPF_FUNC_timer_set_callback) {
		struct bpf_verifier_state *async_cb;

		/* there is no real recursion here. timer callbacks are async */
		env->subprog_info[subprog].is_async_cb = true;
		async_cb = push_async_cb(env, env->subprog_info[subprog].start,
					 *insn_idx, subprog);
		if (!async_cb)
			return -EFAULT;
		callee = async_cb->frame[0];
		callee->async_entry_cnt = caller->async_entry_cnt + 1;

		/* Convert bpf_timer_set_callback() args into timer callback args */
		err = set_callee_state_cb(env, caller, callee, *insn_idx);
		if (err)
			return err;

		clear_caller_saved_regs(env, caller->regs);
		mark_reg_unknown(env, caller->regs, BPF_REG_0);
		caller->regs[BPF_REG_0].subreg_def = DEF_NOT_SUBREG;
		/* continue with next insn after call */
		return 0;
	}

	callee = kzalloc(sizeof(*callee), GFP_KERNEL);
	if (!callee)
		return -ENOMEM;
	state->frame[state->curframe + 1] = callee;

	/* callee cannot access r0, r6 - r9 for reading and has to write
	 * into its own stack before reading from it.
	 * callee can read/write into caller's stack
	 */
	init_func_state(env, callee,
			/* remember the callsite, it will be used by bpf_exit */
			*insn_idx /* callsite */,
			state->curframe + 1 /* frameno within this callchain */,
			subprog /* subprog number within this prog */);

	/* Transfer references to the callee */
	err = copy_reference_state(callee, caller);
	if (err)
		return err;

	err = set_callee_state_cb(env, caller, callee, *insn_idx);
	if (err)
		return err;

	clear_caller_saved_regs(env, caller->regs);

	/* only increment it after check_reg_arg() finished */
	state->curframe++;

	/* and go analyze first insn of the callee */
	*insn_idx = env->subprog_info[subprog].start - 1;

	if (env->log.level & BPF_LOG_LEVEL) {
		verbose(env, "caller:\n");
		print_verifier_state(env, caller, true);
		verbose(env, "callee:\n");
		print_verifier_state(env, callee, true);
	}
	return 0;
}


static int check_mem_access(struct bpf_verifier_env *env, int insn_idx, u32 regno,
			    int off, int bpf_size, enum bpf_access_type t,
			    int value_regno, bool strict_alignment_once)
{
	struct bpf_reg_state *regs = cur_regs(env);
	struct bpf_reg_state *reg = regs + regno;
	struct bpf_func_state *state;
	int size, err = 0;

	size = bpf_size_to_bytes(bpf_size);
	if (size < 0)
		return size;

	/* alignment checks will add in reg->off themselves */
	err = check_ptr_alignment(env, reg, off, size, strict_alignment_once);
	if (err)
		return err;

	/* for access checks, reg->off is just part of off */
	off += reg->off;

	if (reg->type == PTR_TO_MAP_KEY) {
		if (t == BPF_WRITE) {
			verbose(env, "write to change key R%d not allowed\n", regno);
			return -EACCES;
		}

		err = check_mem_region_access(env, regno, off, size,
					      reg->map_ptr->key_size, false);
		if (err)
			return err;
		if (value_regno >= 0)
			mark_reg_unknown(env, regs, value_regno);
	} else if (reg->type == PTR_TO_MAP_VALUE) {
		if (t == BPF_WRITE && value_regno >= 0 &&
		    is_pointer_value(env, value_regno)) {
			verbose(env, "R%d leaks addr into map\n", value_regno);
			return -EACCES;
		}
		err = check_map_access_type(env, regno, off, size, t);
		if (err)
			return err;
		err = check_map_access(env, regno, off, size, false);
		if (!err && t == BPF_READ && value_regno >= 0) {
			struct bpf_map *map = reg->map_ptr;

			/* if map is read-only, track its contents as scalars */
			if (tnum_is_const(reg->var_off) &&
			    bpf_map_is_rdonly(map) &&
			    map->ops->map_direct_value_addr) {
				int map_off = off + reg->var_off.value;
				u64 val = 0;

				err = bpf_map_direct_read(map, map_off, size,
							  &val);
				if (err)
					return err;

				regs[value_regno].type = SCALAR_VALUE;
				__mark_reg_known(&regs[value_regno], val);
			} else {
				mark_reg_unknown(env, regs, value_regno);
			}
		}
	} else if (base_type(reg->type) == PTR_TO_MEM) {
		bool rdonly_mem = type_is_rdonly_mem(reg->type);

		if (type_may_be_null(reg->type)) {
			verbose(env, "R%d invalid mem access '%s'\n", regno,
				reg_type_str(env, reg->type));
			return -EACCES;
		}

		if (t == BPF_WRITE && rdonly_mem) {
			verbose(env, "R%d cannot write into %s\n",
				regno, reg_type_str(env, reg->type));
			return -EACCES;
		}

		if (t == BPF_WRITE && value_regno >= 0 &&
		    is_pointer_value(env, value_regno)) {
			verbose(env, "R%d leaks addr into mem\n", value_regno);
			return -EACCES;
		}

		err = check_mem_region_access(env, regno, off, size,
					      reg->mem_size, false);
		if (!err && value_regno >= 0 && (t == BPF_READ || rdonly_mem))
			mark_reg_unknown(env, regs, value_regno);
	} else if (reg->type == PTR_TO_CTX) {
		enum bpf_reg_type reg_type = SCALAR_VALUE;
		struct btf *btf = NULL;
		u32 btf_id = 0;

		if (t == BPF_WRITE && value_regno >= 0 &&
		    is_pointer_value(env, value_regno)) {
			verbose(env, "R%d leaks addr into ctx\n", value_regno);
			return -EACCES;
		}

		err = check_ctx_reg(env, reg, regno);
		if (err < 0)
			return err;

		err = check_ctx_access(env, insn_idx, off, size, t, &reg_type, &btf, &btf_id);
		if (err)
			verbose_linfo(env, insn_idx, "; ");
		if (!err && t == BPF_READ && value_regno >= 0) {
			/* ctx access returns either a scalar, or a
			 * PTR_TO_PACKET[_META,_END]. In the latter
			 * case, we know the offset is zero.
			 */
			if (reg_type == SCALAR_VALUE) {
				mark_reg_unknown(env, regs, value_regno);
			} else {
				mark_reg_known_zero(env, regs,
						    value_regno);
				if (type_may_be_null(reg_type))
					regs[value_regno].id = ++env->id_gen;
				/* A load of ctx field could have different
				 * actual load size with the one encoded in the
				 * insn. When the dst is PTR, it is for sure not
				 * a sub-register.
				 */
				regs[value_regno].subreg_def = DEF_NOT_SUBREG;
				if (base_type(reg_type) == PTR_TO_BTF_ID) {
					regs[value_regno].btf = btf;
					regs[value_regno].btf_id = btf_id;
				}
			}
			regs[value_regno].type = reg_type;
		}

	} else if (reg->type == PTR_TO_STACK) {
		/* Basic bounds checks. */
		err = check_stack_access_within_bounds(env, regno, off, size, ACCESS_DIRECT, t);
		if (err)
			return err;

		state = func(env, reg);
		err = update_stack_depth(env, state, off);
		if (err)
			return err;

		if (t == BPF_READ)
			err = check_stack_read(env, regno, off, size,
					       value_regno);
		else
			err = check_stack_write(env, regno, off, size,
						value_regno, insn_idx);
	} else if (reg_is_pkt_pointer(reg)) {
		if (t == BPF_WRITE && !may_access_direct_pkt_data(env, NULL, t)) {
			verbose(env, "cannot write into packet\n");
			return -EACCES;
		}
		if (t == BPF_WRITE && value_regno >= 0 &&
		    is_pointer_value(env, value_regno)) {
			verbose(env, "R%d leaks addr into packet\n",
				value_regno);
			return -EACCES;
		}
		err = check_packet_access(env, regno, off, size, false);
		if (!err && t == BPF_READ && value_regno >= 0)
			mark_reg_unknown(env, regs, value_regno);
	} else if (reg->type == PTR_TO_FLOW_KEYS) {
		if (t == BPF_WRITE && value_regno >= 0 &&
		    is_pointer_value(env, value_regno)) {
			verbose(env, "R%d leaks addr into flow keys\n",
				value_regno);
			return -EACCES;
		}

		err = check_flow_keys_access(env, off, size);
		if (!err && t == BPF_READ && value_regno >= 0)
			mark_reg_unknown(env, regs, value_regno);
	} else if (type_is_sk_pointer(reg->type)) {
		if (t == BPF_WRITE) {
			verbose(env, "R%d cannot write into %s\n",
				regno, reg_type_str(env, reg->type));
			return -EACCES;
		}
		err = check_sock_access(env, insn_idx, regno, off, size, t);
		if (!err && value_regno >= 0)
			mark_reg_unknown(env, regs, value_regno);
	} else if (reg->type == PTR_TO_TP_BUFFER) {
		err = check_tp_buffer_access(env, reg, regno, off, size);
		if (!err && t == BPF_READ && value_regno >= 0)
			mark_reg_unknown(env, regs, value_regno);
	} else if (reg->type == PTR_TO_BTF_ID) {
		err = check_ptr_to_btf_access(env, regs, regno, off, size, t,
					      value_regno);
	} else if (reg->type == CONST_PTR_TO_MAP) {
		err = check_ptr_to_map_access(env, regs, regno, off, size, t,
					      value_regno);
	} else if (base_type(reg->type) == PTR_TO_BUF) {
		bool rdonly_mem = type_is_rdonly_mem(reg->type);
		const char *buf_info;
		u32 *max_access;

		if (rdonly_mem) {
			if (t == BPF_WRITE) {
				verbose(env, "R%d cannot write into %s\n",
					regno, reg_type_str(env, reg->type));
				return -EACCES;
			}
			buf_info = "rdonly";
			max_access = &env->prog->aux->max_rdonly_access;
		} else {
			buf_info = "rdwr";
			max_access = &env->prog->aux->max_rdwr_access;
		}

		err = check_buffer_access(env, reg, regno, off, size, false,
					  buf_info, max_access);

		if (!err && value_regno >= 0 && (rdonly_mem || t == BPF_READ))
			mark_reg_unknown(env, regs, value_regno);
	} else {
		verbose(env, "R%d invalid mem access '%s'\n", regno,
			reg_type_str(env, reg->type));
		return -EACCES;
	}

	if (!err && size < BPF_REG_SIZE && value_regno >= 0 && t == BPF_READ &&
	    regs[value_regno].type == SCALAR_VALUE) {
		/* b/h/w load zero-extends, mark upper bits as known 0 */
		coerce_reg_to_size(&regs[value_regno], size);
	}
	return err;
}


flush_changed_page_bitmaps()
{
	if (xtrabackup_incremental && have_changed_page_bitmaps &&
	    !xtrabackup_incremental_force_scan) {
		xb_mysql_query(mysql_connection,
			"FLUSH NO_WRITE_TO_BINLOG CHANGED_PAGE_BITMAPS", false);
	}
	return(true);
}


lock_tables_for_backup(MYSQL *connection, int timeout, int retry_count)
{
	if (have_lock_wait_timeout) {
		char query[200];

		ut_snprintf(query, sizeof(query),
			    "SET SESSION lock_wait_timeout=%d", timeout);

		xb_mysql_query(connection, query, false);
	}

	if (have_backup_locks) {
		for (int i = 0; i <= retry_count; ++i) {
			msg_ts("Executing LOCK TABLES FOR BACKUP...\n");
			xb_mysql_query(connection, "LOCK TABLES FOR BACKUP",
				       false, false);
			uint err = mysql_errno(connection);
			if (err == ER_LOCK_WAIT_TIMEOUT) {
				os_thread_sleep(1000000);
				continue;
			}
			if (err == 0) {
				tables_locked = true;
			}
			break;
		}
		if (!tables_locked) {
			exit(EXIT_FAILURE);
		}
		return(true);
	}

	msg_ts("Error: LOCK TABLES FOR BACKUP is not supported.\n");

	return(false);
}


size_t format_append(std::string& dest, const char *format, ...) {
	char* buffer;
	int len = 0;
	va_list ap;

	va_start(ap, format);
	ut_a((len = vasprintf(&buffer, format, ap)) != -1 );
	va_end(ap);

	dest += buffer;
	free(buffer);
	return (size_t)len;
}


read_mysql_variables(MYSQL *connection, const char *query, mysql_variable *vars,
	bool vertical_result)
{
	MYSQL_RES *mysql_result = xb_mysql_query(connection, query, true);
	read_mysql_variables_from_result(mysql_result, vars, vertical_result);
	mysql_free_result(mysql_result);
}


bool format_time(time_t time, char *dest, size_t max_size)
{
	tm tm;
	localtime_r(&time, &tm);
	return strftime(dest, max_size,
		 "%Y-%m-%d %H:%M:%S", &tm) != 0;
}


char* get_xtrabackup_info(MYSQL *connection)
{
	const char *uuid = get_backup_uuid(connection);
	char *server_version = read_mysql_one_value(connection, "SELECT VERSION()");

	static const size_t time_buf_size = 100;
	char buf_start_time[time_buf_size];
	char buf_end_time[time_buf_size];

	format_time(history_start_time, buf_start_time, time_buf_size);
	format_time(history_end_time, buf_end_time, time_buf_size);

	ut_a(uuid);
	ut_a(server_version);
	char* result = NULL;
	asprintf(&result,
		"uuid = %s\n"
		"name = %s\n"
		"tool_name = %s\n"
		"tool_command = %s\n"
		"tool_version = %s\n"
		"ibbackup_version = %s\n"
		"server_version = %s\n"
		"start_time = %s\n"
		"end_time = %s\n"
		"lock_time = %ld\n"
		"binlog_pos = %s\n"
		"innodb_from_lsn = " LSN_PF "\n"
		"innodb_to_lsn = " LSN_PF "\n"
		"partial = %s\n"
		"incremental = %s\n"
		"format = %s\n"
		"compact = %s\n"
		"compressed = %s\n"
		"encrypted = %s\n",
		uuid, /* uuid */
		opt_history ? opt_history : "",  /* name */
		tool_name,  /* tool_name */
		tool_args,  /* tool_command */
		XTRABACKUP_VERSION,  /* tool_version */
		XTRABACKUP_VERSION,  /* ibbackup_version */
		server_version,  /* server_version */
		buf_start_time,  /* start_time */
		buf_end_time,  /* end_time */
		(long int)history_lock_time, /* lock_time */
		mysql_binlog_position ?
			mysql_binlog_position : "", /* binlog_pos */
		incremental_lsn, /* innodb_from_lsn */
		metadata_to_lsn, /* innodb_to_lsn */
		(xtrabackup_tables /* partial */
		 || xtrabackup_tables_exclude
		 || xtrabackup_tables_file
		 || xtrabackup_databases
		 || xtrabackup_databases_exclude
		 || xtrabackup_databases_file) ? "Y" : "N",
		xtrabackup_incremental ? "Y" : "N", /* incremental */
		xb_stream_format_name[xtrabackup_stream_fmt], /* format */
		xtrabackup_compact ? "Y" : "N", /* compact */
		xtrabackup_compress ? "compressed" : "N", /* compressed */
		xtrabackup_encrypt ? "Y" : "N"); /* encrypted */

	free(server_version);
	return result;
}


get_mysql_vars(MYSQL *connection)
{
	char *gtid_mode_var = NULL;
	char *version_var = NULL;
	char *version_comment_var = NULL;
	char *innodb_version_var = NULL;
	char *have_backup_locks_var = NULL;
	char *have_backup_safe_binlog_info_var = NULL;
	char *log_bin_var = NULL;
	char *lock_wait_timeout_var= NULL;
	char *wsrep_on_var = NULL;
	char *slave_parallel_workers_var = NULL;
	char *gtid_slave_pos_var = NULL;
	char *innodb_buffer_pool_filename_var = NULL;
	char *datadir_var = NULL;
	char *innodb_log_group_home_dir_var = NULL;
	char *innodb_log_file_size_var = NULL;
	char *innodb_log_files_in_group_var = NULL;
	char *innodb_data_file_path_var = NULL;
	char *innodb_data_home_dir_var = NULL;
	char *innodb_undo_directory_var = NULL;
	char *innodb_page_size_var = NULL;
	char *innodb_log_checksums_var = NULL;
	char *innodb_log_checksum_algorithm_var = NULL;
	char *innodb_checksum_algorithm_var = NULL;
	char *innodb_track_changed_pages_var = NULL;
	char *server_uuid_var = NULL;

	unsigned long server_version = mysql_get_server_version(connection);

	bool ret = true;

	mysql_variable mysql_vars[] = {
		{"have_backup_locks", &have_backup_locks_var},
		{"have_backup_safe_binlog_info",
		 &have_backup_safe_binlog_info_var},
		{"log_bin", &log_bin_var},
		{"lock_wait_timeout", &lock_wait_timeout_var},
		{"gtid_mode", &gtid_mode_var},
		{"version", &version_var},
		{"version_comment", &version_comment_var},
		{"innodb_version", &innodb_version_var},
		{"wsrep_on", &wsrep_on_var},
		{"slave_parallel_workers", &slave_parallel_workers_var},
		{"gtid_slave_pos", &gtid_slave_pos_var},
		{"innodb_buffer_pool_filename",
			&innodb_buffer_pool_filename_var},
		{"datadir", &datadir_var},
		{"innodb_log_group_home_dir", &innodb_log_group_home_dir_var},
		{"innodb_log_file_size", &innodb_log_file_size_var},
		{"innodb_log_files_in_group", &innodb_log_files_in_group_var},
		{"innodb_data_file_path", &innodb_data_file_path_var},
		{"innodb_data_home_dir", &innodb_data_home_dir_var},
		{"innodb_undo_directory", &innodb_undo_directory_var},
		{"innodb_page_size", &innodb_page_size_var},
		{"innodb_log_checksums", &innodb_log_checksums_var},
		{"innodb_log_checksum_algorithm",
			&innodb_log_checksum_algorithm_var},
		{"innodb_checksum_algorithm", &innodb_checksum_algorithm_var},
		{"innodb_track_changed_pages", &innodb_track_changed_pages_var},
		{"server_uuid", &server_uuid_var},
		{NULL, NULL}
	};

	read_mysql_variables(connection, "SHOW VARIABLES",
				mysql_vars, true);

	if (have_backup_locks_var != NULL && !opt_no_backup_locks) {
		have_backup_locks = true;
	}

	if (have_backup_safe_binlog_info_var == NULL &&
	    opt_binlog_info == BINLOG_INFO_LOCKLESS) {

		msg("Error: --binlog-info=LOCKLESS is not supported by the "
		    "server\n");
		return(false);
	}

	if (lock_wait_timeout_var != NULL) {
		have_lock_wait_timeout = true;
	}

	if (wsrep_on_var != NULL) {
		have_galera_enabled = true;
	}

	/* Check server version compatibility and detect server flavor */

	if (!(ret = check_server_version(server_version, version_var,
					 version_comment_var,
					 innodb_version_var))) {
		goto out;
	}

	if (server_version > 50500) {
		have_flush_engine_logs = true;
	}

	if (slave_parallel_workers_var != NULL
		&& atoi(slave_parallel_workers_var) > 0) {
		have_multi_threaded_slave = true;
	}

	if (innodb_buffer_pool_filename_var != NULL) {
		buffer_pool_filename = strdup(innodb_buffer_pool_filename_var);
	}

	if ((gtid_mode_var && strcmp(gtid_mode_var, "ON") == 0) ||
	    (gtid_slave_pos_var && *gtid_slave_pos_var)) {
		have_gtid_slave = true;
	}

	if (opt_binlog_info == BINLOG_INFO_AUTO) {

		if (have_backup_safe_binlog_info_var != NULL &&
		    !have_gtid_slave)
			opt_binlog_info = BINLOG_INFO_LOCKLESS;
		else if (log_bin_var != NULL && !strcmp(log_bin_var, "ON"))
			opt_binlog_info = BINLOG_INFO_ON;
		else
			opt_binlog_info = BINLOG_INFO_OFF;
	}

	msg("Using server version %s\n", version_var);

	if (!(ret = detect_mysql_capabilities_for_backup())) {
		goto out;
	}

	/* make sure datadir value is the same in configuration file */
	if (check_if_param_set("datadir")) {
		if (!directory_exists(mysql_data_home, false)) {
			msg("Warning: option 'datadir' points to "
			    "nonexistent directory '%s'\n", mysql_data_home);
		}
		if (!directory_exists(datadir_var, false)) {
			msg("Warning: MySQL variable 'datadir' points to "
			    "nonexistent directory '%s'\n", datadir_var);
		}
		if (!equal_paths(mysql_data_home, datadir_var)) {
			msg("Warning: option 'datadir' has different "
				"values:\n"
				"  '%s' in defaults file\n"
				"  '%s' in SHOW VARIABLES\n",
				mysql_data_home, datadir_var);
		}
	}

	/* get some default values is they are missing from my.cnf */
	if (!check_if_param_set("datadir") && datadir_var && *datadir_var) {
		strmake(mysql_real_data_home, datadir_var, FN_REFLEN - 1);
		mysql_data_home= mysql_real_data_home;
	}

	if (!check_if_param_set("innodb_data_file_path")
	    && innodb_data_file_path_var && *innodb_data_file_path_var) {
		innobase_data_file_path = my_strdup(PSI_NOT_INSTRUMENTED,
			innodb_data_file_path_var, MYF(MY_FAE));
	}

	if (!check_if_param_set("innodb_data_home_dir")
	    && innodb_data_home_dir_var && *innodb_data_home_dir_var) {
		innobase_data_home_dir = my_strdup(PSI_NOT_INSTRUMENTED,
			innodb_data_home_dir_var, MYF(MY_FAE));
	}

	if (!check_if_param_set("innodb_log_group_home_dir")
	    && innodb_log_group_home_dir_var
	    && *innodb_log_group_home_dir_var) {
		srv_log_group_home_dir = my_strdup(PSI_NOT_INSTRUMENTED,
			innodb_log_group_home_dir_var, MYF(MY_FAE));
	}

	if (!check_if_param_set("innodb_undo_directory")
	    && innodb_undo_directory_var && *innodb_undo_directory_var) {
		srv_undo_dir = my_strdup(PSI_NOT_INSTRUMENTED,
			innodb_undo_directory_var, MYF(MY_FAE));
	}

	if (!check_if_param_set("innodb_log_files_in_group")
	    && innodb_log_files_in_group_var) {
		char *endptr;

		innobase_log_files_in_group = strtol(
			innodb_log_files_in_group_var, &endptr, 10);
		ut_ad(*endptr == 0);
	}

	if (!check_if_param_set("innodb_log_file_size")
	    && innodb_log_file_size_var) {
		char *endptr;

		innobase_log_file_size = strtoll(
			innodb_log_file_size_var, &endptr, 10);
		ut_ad(*endptr == 0);
	}

	if (!check_if_param_set("innodb_page_size") && innodb_page_size_var) {
		char *endptr;

		innobase_page_size = strtoll(
			innodb_page_size_var, &endptr, 10);
		ut_ad(*endptr == 0);
	}

	if (!innodb_log_checksum_algorithm_specified &&
		innodb_log_checksum_algorithm_var) {
		for (uint i = 0;
		     i < innodb_checksum_algorithm_typelib.count;
		     i++) {
			if (strcasecmp(innodb_log_checksum_algorithm_var,
			    innodb_checksum_algorithm_typelib.type_names[i])
			    == 0) {
				srv_log_checksum_algorithm = i;
			}
		}
	}

	if (!innodb_checksum_algorithm_specified &&
		innodb_checksum_algorithm_var) {
		for (uint i = 0;
		     i < innodb_checksum_algorithm_typelib.count;
		     i++) {
			if (strcasecmp(innodb_checksum_algorithm_var,
			    innodb_checksum_algorithm_typelib.type_names[i])
			    == 0) {
				srv_checksum_algorithm = i;
			}
		}
	}

	if (!innodb_log_checksum_algorithm_specified &&
		innodb_log_checksums_var) {
		if (strcasecmp(innodb_log_checksums_var, "ON") == 0) {
			srv_log_checksum_algorithm =
				SRV_CHECKSUM_ALGORITHM_STRICT_CRC32;
		} else {
			srv_log_checksum_algorithm =
				SRV_CHECKSUM_ALGORITHM_NONE;
		}
	}

	memset(server_uuid, 0, ENCRYPTION_SERVER_UUID_LEN + 1);
	if (server_uuid_var != NULL) {
		strncpy(server_uuid, server_uuid_var,
			ENCRYPTION_SERVER_UUID_LEN);
	}

	if (innodb_track_changed_pages_var != NULL &&
	    strcasecmp(innodb_track_changed_pages_var, "ON") == 0) {
	  have_changed_page_bitmaps = true;
	}

out:
	free_mysql_variables(mysql_vars);

	return(ret);
}


read_mysql_one_value(MYSQL *connection, const char *query)
{
	MYSQL_RES *mysql_result;
	MYSQL_ROW row;
	char *result = NULL;

	mysql_result = xb_mysql_query(connection, query, true);

	ut_ad(mysql_num_fields(mysql_result) == 1);

	if ((row = mysql_fetch_row(mysql_result))) {
		result = strdup(row[0]);
	}

	mysql_free_result(mysql_result);

	return(result);
}


static void compute_ideal_colors_and_weights_2_comp(
	const block_size_descriptor& bsd,
	const image_block& blk,
	const error_weight_block& ewb,
	const partition_info& pi,
	endpoints_and_weights& ei,
	int component1,
	int component2
) {
	int partition_count = pi.partition_count;
	ei.ep.partition_count = partition_count;
	promise(partition_count > 0);

	int texel_count = bsd.texel_count;
	promise(texel_count > 0);

	partition_metrics pms[BLOCK_MAX_PARTITIONS];

	const float *error_weights;
	const float* data_vr = nullptr;
	const float* data_vg = nullptr;
	if (component1 == 0 && component2 == 1)
	{
		error_weights = ewb.texel_weight_rg;
		data_vr = blk.data_r;
		data_vg = blk.data_g;
	}
	else if (component1 == 0 && component2 == 2)
	{
		error_weights = ewb.texel_weight_rb;
		data_vr = blk.data_r;
		data_vg = blk.data_b;
	}
	else // (component1 == 1 && component2 == 2)
	{
		error_weights = ewb.texel_weight_gb;
		data_vr = blk.data_g;
		data_vg = blk.data_b;
	}

	float lowparam[BLOCK_MAX_PARTITIONS] { 1e10f, 1e10f, 1e10f, 1e10f };
	float highparam[BLOCK_MAX_PARTITIONS] { -1e10f, -1e10f, -1e10f, -1e10f };

	line2 lines[BLOCK_MAX_PARTITIONS];
	float scale[BLOCK_MAX_PARTITIONS];
	float length_squared[BLOCK_MAX_PARTITIONS];

	compute_avgs_and_dirs_2_comp(pi, blk, ewb, component1, component2, pms);

	for (int i = 0; i < partition_count; i++)
	{
		vfloat4 dir = pms[i].dir.swz<0, 1>();
		if (hadd_s(dir) < 0.0f)
		{
			dir = vfloat4::zero() - dir;
		}

		lines[i].a = pms[i].avg.swz<0, 1>();
		lines[i].b = normalize_safe(dir, unit2());
	}

	for (int i = 0; i < texel_count; i++)
	{
		if (error_weights[i] > 1e-10f)
		{
			int partition = pi.partition_of_texel[i];
			vfloat4 point = vfloat2(data_vr[i], data_vg[i]) * pms[partition].color_scale.swz<0, 1>();
			line2 l = lines[partition];
			float param = dot_s(point - l.a, l.b);
			ei.weights[i] = param;

			lowparam[partition] = astc::min(param, lowparam[partition]);
			highparam[partition] = astc::max(param, highparam[partition]);
		}
		else
		{
			ei.weights[i] = -1e38f;
		}
	}

	vfloat4 lowvalues[BLOCK_MAX_PARTITIONS];
	vfloat4 highvalues[BLOCK_MAX_PARTITIONS];

	for (int i = 0; i < partition_count; i++)
	{
		float length = highparam[i] - lowparam[i];
		if (length < 0.0f) // Case for when none of the texels had any weight
		{
			lowparam[i] = 0.0f;
			highparam[i] = 1e-7f;
		}

		// It is possible for a uniform-color partition to produce length=0; this causes NaN issues
		// so set to a small value to avoid this problem.
		length = astc::max(length, 1e-7f);
		length_squared[i] = length * length;
		scale[i] = 1.0f / length;

		vfloat4 ep0 = lines[i].a + lines[i].b * lowparam[i];
		vfloat4 ep1 = lines[i].a + lines[i].b * highparam[i];

		ep0 = ep0.swz<0, 1>() / pms[i].color_scale;

		ep1 = ep1.swz<0, 1>() / pms[i].color_scale;

		lowvalues[i] = ep0;
		highvalues[i] = ep1;
	}

	vmask4 comp1_mask = vint4::lane_id() == vint4(component1);
	vmask4 comp2_mask = vint4::lane_id() == vint4(component2);
	for (int i = 0; i < partition_count; i++)
	{
		vfloat4 ep0 = select(blk.data_min, vfloat4(lowvalues[i].lane<0>()), comp1_mask);
		vfloat4 ep1 = select(blk.data_max, vfloat4(highvalues[i].lane<0>()), comp1_mask);

		ei.ep.endpt0[i] = select(ep0, vfloat4(lowvalues[i].lane<1>()), comp2_mask);
		ei.ep.endpt1[i] = select(ep1, vfloat4(highvalues[i].lane<1>()), comp2_mask);
	}

	bool is_constant_wes = true;
	float constant_wes = length_squared[pi.partition_of_texel[0]] * error_weights[0];

	for (int i = 0; i < texel_count; i++)
	{
		int partition = pi.partition_of_texel[i];
		float idx = (ei.weights[i] - lowparam[partition]) * scale[partition];
		idx = astc::clamp1f(idx);

		ei.weights[i] = idx;
		ei.weight_error_scale[i] = length_squared[partition] * error_weights[i];
		assert(!astc::isnan(ei.weight_error_scale[i]));

		is_constant_wes = is_constant_wes && ei.weight_error_scale[i] == constant_wes;
	}

	// Zero initialize any SIMD over-fetch
	int texel_count_simd = round_up_to_simd_multiple_vla(texel_count);
	for (int i = texel_count; i < texel_count_simd; i++)
	{
		ei.weights[i] = 0.0f;
		ei.weight_error_scale[i] = 0.0f;
	}

	ei.is_constant_weight_error_scale = is_constant_wes;
}


changeDependenciesOf(Oid classId, Oid oldObjectId,
					 Oid newObjectId)
{
	long		count = 0;
	Relation	depRel;
	ScanKeyData key[2];
	SysScanDesc scan;
	HeapTuple	tup;

	depRel = table_open(DependRelationId, RowExclusiveLock);

	ScanKeyInit(&key[0],
				Anum_pg_depend_classid,
				BTEqualStrategyNumber, F_OIDEQ,
				ObjectIdGetDatum(classId));
	ScanKeyInit(&key[1],
				Anum_pg_depend_objid,
				BTEqualStrategyNumber, F_OIDEQ,
				ObjectIdGetDatum(oldObjectId));

	scan = systable_beginscan(depRel, DependDependerIndexId, true,
							  NULL, 2, key);

	while (HeapTupleIsValid((tup = systable_getnext(scan))))
	{
		Form_pg_depend depform;

		/* make a modifiable copy */
		tup = heap_copytuple(tup);
		depform = (Form_pg_depend) GETSTRUCT(tup);

		depform->objid = newObjectId;

		CatalogTupleUpdate(depRel, &tup->t_self, tup);

		heap_freetuple(tup);

		count++;
	}

	systable_endscan(scan);

	table_close(depRel, RowExclusiveLock);

	return count;
}


init_sequence(Oid relid, SeqTable *p_elm, Relation *p_rel)
{
	SeqTable	elm;
	Relation	seqrel;
	bool		found;

	/* Find or create a hash table entry for this sequence */
	if (seqhashtab == NULL)
		create_seq_hashtable();

	elm = (SeqTable) hash_search(seqhashtab, &relid, HASH_ENTER, &found);

	/*
	 * Initialize the new hash table entry if it did not exist already.
	 *
	 * NOTE: seqhashtab entries are stored for the life of a backend (unless
	 * explicitly discarded with DISCARD). If the sequence itself is deleted
	 * then the entry becomes wasted memory, but it's small enough that this
	 * should not matter.
	 */
	if (!found)
	{
		/* relid already filled in */
		elm->filenumber = InvalidRelFileNumber;
		elm->lxid = InvalidLocalTransactionId;
		elm->last_valid = false;
		elm->last = elm->cached = 0;
	}

	/*
	 * Open the sequence relation.
	 */
	seqrel = lock_and_open_sequence(elm);

	if (seqrel->rd_rel->relkind != RELKIND_SEQUENCE)
		ereport(ERROR,
				(errcode(ERRCODE_WRONG_OBJECT_TYPE),
				 errmsg("\"%s\" is not a sequence",
						RelationGetRelationName(seqrel))));

	/*
	 * If the sequence has been transactionally replaced since we last saw it,
	 * discard any cached-but-unissued values.  We do not touch the currval()
	 * state, however.
	 */
	if (seqrel->rd_rel->relfilenode != elm->filenumber)
	{
		elm->filenumber = seqrel->rd_rel->relfilenode;
		elm->cached = elm->last;
	}

	/* Return results */
	*p_elm = elm;
	*p_rel = seqrel;
}


AlterForeignServer(AlterForeignServerStmt *stmt)
{
	Relation	rel;
	HeapTuple	tp;
	Datum		repl_val[Natts_pg_foreign_server];
	bool		repl_null[Natts_pg_foreign_server];
	bool		repl_repl[Natts_pg_foreign_server];
	Oid			srvId;
	Form_pg_foreign_server srvForm;
	ObjectAddress address;

	rel = table_open(ForeignServerRelationId, RowExclusiveLock);

	tp = SearchSysCacheCopy1(FOREIGNSERVERNAME,
							 CStringGetDatum(stmt->servername));

	if (!HeapTupleIsValid(tp))
		ereport(ERROR,
				(errcode(ERRCODE_UNDEFINED_OBJECT),
				 errmsg("server \"%s\" does not exist", stmt->servername)));

	srvForm = (Form_pg_foreign_server) GETSTRUCT(tp);
	srvId = srvForm->oid;

	/*
	 * Only owner or a superuser can ALTER a SERVER.
	 */
	if (!pg_foreign_server_ownercheck(srvId, GetUserId()))
		aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_FOREIGN_SERVER,
					   stmt->servername);

	memset(repl_val, 0, sizeof(repl_val));
	memset(repl_null, false, sizeof(repl_null));
	memset(repl_repl, false, sizeof(repl_repl));

	if (stmt->has_version)
	{
		/*
		 * Change the server VERSION string.
		 */
		if (stmt->version)
			repl_val[Anum_pg_foreign_server_srvversion - 1] =
				CStringGetTextDatum(stmt->version);
		else
			repl_null[Anum_pg_foreign_server_srvversion - 1] = true;

		repl_repl[Anum_pg_foreign_server_srvversion - 1] = true;
	}

	if (stmt->options)
	{
		ForeignDataWrapper *fdw = GetForeignDataWrapper(srvForm->srvfdw);
		Datum		datum;
		bool		isnull;

		/* Extract the current srvoptions */
		datum = SysCacheGetAttr(FOREIGNSERVEROID,
								tp,
								Anum_pg_foreign_server_srvoptions,
								&isnull);
		if (isnull)
			datum = PointerGetDatum(NULL);

		/* Prepare the options array */
		datum = transformGenericOptions(ForeignServerRelationId,
										datum,
										stmt->options,
										fdw->fdwvalidator);

		if (PointerIsValid(DatumGetPointer(datum)))
			repl_val[Anum_pg_foreign_server_srvoptions - 1] = datum;
		else
			repl_null[Anum_pg_foreign_server_srvoptions - 1] = true;

		repl_repl[Anum_pg_foreign_server_srvoptions - 1] = true;
	}

	/* Everything looks good - update the tuple */
	tp = heap_modify_tuple(tp, RelationGetDescr(rel),
						   repl_val, repl_null, repl_repl);

	CatalogTupleUpdate(rel, &tp->t_self, tp);

	InvokeObjectPostAlterHook(ForeignServerRelationId, srvId, 0);

	ObjectAddressSet(address, ForeignServerRelationId, srvId);

	heap_freetuple(tp);

	table_close(rel, RowExclusiveLock);

	return address;
}


RemoveStatisticsById(Oid statsOid)
{
	Relation	relation;
	HeapTuple	tup;
	Form_pg_statistic_ext statext;
	Oid			relid;

	/*
	 * First delete the pg_statistic_ext_data tuples holding the actual
	 * statistical data. There might be data with/without inheritance, so
	 * attempt deleting both.
	 */
	RemoveStatisticsDataById(statsOid, true);
	RemoveStatisticsDataById(statsOid, false);

	/*
	 * Delete the pg_statistic_ext tuple.  Also send out a cache inval on the
	 * associated table, so that dependent plans will be rebuilt.
	 */
	relation = table_open(StatisticExtRelationId, RowExclusiveLock);

	tup = SearchSysCache1(STATEXTOID, ObjectIdGetDatum(statsOid));

	if (!HeapTupleIsValid(tup)) /* should not happen */
		elog(ERROR, "cache lookup failed for statistics object %u", statsOid);

	statext = (Form_pg_statistic_ext) GETSTRUCT(tup);
	relid = statext->stxrelid;

	CacheInvalidateRelcacheByRelid(relid);

	CatalogTupleDelete(relation, &tup->t_self);

	ReleaseSysCache(tup);

	table_close(relation, RowExclusiveLock);
}


AlterSchemaOwner_oid(Oid oid, Oid newOwnerId)
{
	HeapTuple	tup;
	Relation	rel;

	rel = table_open(NamespaceRelationId, RowExclusiveLock);

	tup = SearchSysCache1(NAMESPACEOID, ObjectIdGetDatum(oid));
	if (!HeapTupleIsValid(tup))
		elog(ERROR, "cache lookup failed for schema %u", oid);

	AlterSchemaOwner_internal(tup, rel, newOwnerId);

	ReleaseSysCache(tup);

	table_close(rel, RowExclusiveLock);
}


GetIntoRelEFlags(IntoClause *intoClause)
{
	int			flags = 0;

	if (intoClause->skipData)
		flags |= EXEC_FLAG_WITH_NO_DATA;

	return flags;
}


get_index_constraint(Oid indexId)
{
	Oid			constraintId = InvalidOid;
	Relation	depRel;
	ScanKeyData key[3];
	SysScanDesc scan;
	HeapTuple	tup;

	/* Search the dependency table for the index */
	depRel = table_open(DependRelationId, AccessShareLock);

	ScanKeyInit(&key[0],
				Anum_pg_depend_classid,
				BTEqualStrategyNumber, F_OIDEQ,
				ObjectIdGetDatum(RelationRelationId));
	ScanKeyInit(&key[1],
				Anum_pg_depend_objid,
				BTEqualStrategyNumber, F_OIDEQ,
				ObjectIdGetDatum(indexId));
	ScanKeyInit(&key[2],
				Anum_pg_depend_objsubid,
				BTEqualStrategyNumber, F_INT4EQ,
				Int32GetDatum(0));

	scan = systable_beginscan(depRel, DependDependerIndexId, true,
							  NULL, 3, key);

	while (HeapTupleIsValid(tup = systable_getnext(scan)))
	{
		Form_pg_depend deprec = (Form_pg_depend) GETSTRUCT(tup);

		/*
		 * We assume any internal dependency on a constraint must be what we
		 * are looking for.
		 */
		if (deprec->refclassid == ConstraintRelationId &&
			deprec->refobjsubid == 0 &&
			deprec->deptype == DEPENDENCY_INTERNAL)
		{
			constraintId = deprec->refobjid;
			break;
		}
	}

	systable_endscan(scan);
	table_close(depRel, AccessShareLock);

	return constraintId;
}


generateSerialExtraStmts(CreateStmtContext *cxt, ColumnDef *column,
						 Oid seqtypid, List *seqoptions,
						 bool for_identity, bool col_exists,
						 char **snamespace_p, char **sname_p)
{
	ListCell   *option;
	DefElem    *nameEl = NULL;
	Oid			snamespaceid;
	char	   *snamespace;
	char	   *sname;
	CreateSeqStmt *seqstmt;
	AlterSeqStmt *altseqstmt;
	List	   *attnamelist;
	int			nameEl_idx = -1;

	/*
	 * Determine namespace and name to use for the sequence.
	 *
	 * First, check if a sequence name was passed in as an option.  This is
	 * used by pg_dump.  Else, generate a name.
	 *
	 * Although we use ChooseRelationName, it's not guaranteed that the
	 * selected sequence name won't conflict; given sufficiently long field
	 * names, two different serial columns in the same table could be assigned
	 * the same sequence name, and we'd not notice since we aren't creating
	 * the sequence quite yet.  In practice this seems quite unlikely to be a
	 * problem, especially since few people would need two serial columns in
	 * one table.
	 */
	foreach(option, seqoptions)
	{
		DefElem    *defel = lfirst_node(DefElem, option);

		if (strcmp(defel->defname, "sequence_name") == 0)
		{
			if (nameEl)
				errorConflictingDefElem(defel, cxt->pstate);
			nameEl = defel;
			nameEl_idx = foreach_current_index(option);
		}
	}

	if (nameEl)
	{
		RangeVar   *rv = makeRangeVarFromNameList(castNode(List, nameEl->arg));

		snamespace = rv->schemaname;
		if (!snamespace)
		{
			/* Given unqualified SEQUENCE NAME, select namespace */
			if (cxt->rel)
				snamespaceid = RelationGetNamespace(cxt->rel);
			else
				snamespaceid = RangeVarGetCreationNamespace(cxt->relation);
			snamespace = get_namespace_name(snamespaceid);
		}
		sname = rv->relname;
		/* Remove the SEQUENCE NAME item from seqoptions */
		seqoptions = list_delete_nth_cell(seqoptions, nameEl_idx);
	}
	else
	{
		if (cxt->rel)
			snamespaceid = RelationGetNamespace(cxt->rel);
		else
		{
			snamespaceid = RangeVarGetCreationNamespace(cxt->relation);
			RangeVarAdjustRelationPersistence(cxt->relation, snamespaceid);
		}
		snamespace = get_namespace_name(snamespaceid);
		sname = ChooseRelationName(cxt->relation->relname,
								   column->colname,
								   "seq",
								   snamespaceid,
								   false);
	}

	ereport(DEBUG1,
			(errmsg_internal("%s will create implicit sequence \"%s\" for serial column \"%s.%s\"",
							 cxt->stmtType, sname,
							 cxt->relation->relname, column->colname)));

	/*
	 * Build a CREATE SEQUENCE command to create the sequence object, and add
	 * it to the list of things to be done before this CREATE/ALTER TABLE.
	 */
	seqstmt = makeNode(CreateSeqStmt);
	seqstmt->for_identity = for_identity;
	seqstmt->sequence = makeRangeVar(snamespace, sname, -1);
	seqstmt->sequence->relpersistence = cxt->relation->relpersistence;
	seqstmt->options = seqoptions;

	/*
	 * If a sequence data type was specified, add it to the options.  Prepend
	 * to the list rather than append; in case a user supplied their own AS
	 * clause, the "redundant options" error will point to their occurrence,
	 * not our synthetic one.
	 */
	if (seqtypid)
		seqstmt->options = lcons(makeDefElem("as",
											 (Node *) makeTypeNameFromOid(seqtypid, -1),
											 -1),
								 seqstmt->options);

	/*
	 * If this is ALTER ADD COLUMN, make sure the sequence will be owned by
	 * the table's owner.  The current user might be someone else (perhaps a
	 * superuser, or someone who's only a member of the owning role), but the
	 * SEQUENCE OWNED BY mechanisms will bleat unless table and sequence have
	 * exactly the same owning role.
	 */
	if (cxt->rel)
		seqstmt->ownerId = cxt->rel->rd_rel->relowner;
	else
		seqstmt->ownerId = InvalidOid;

	cxt->blist = lappend(cxt->blist, seqstmt);

	/*
	 * Store the identity sequence name that we decided on.  ALTER TABLE ...
	 * ADD COLUMN ... IDENTITY needs this so that it can fill the new column
	 * with values from the sequence, while the association of the sequence
	 * with the table is not set until after the ALTER TABLE.
	 */
	column->identitySequence = seqstmt->sequence;

	/*
	 * Build an ALTER SEQUENCE ... OWNED BY command to mark the sequence as
	 * owned by this column, and add it to the appropriate list of things to
	 * be done along with this CREATE/ALTER TABLE.  In a CREATE or ALTER ADD
	 * COLUMN, it must be done after the statement because we don't know the
	 * column's attnum yet.  But if we do have the attnum (in AT_AddIdentity),
	 * we can do the marking immediately, which improves some ALTER TABLE
	 * behaviors.
	 */
	altseqstmt = makeNode(AlterSeqStmt);
	altseqstmt->sequence = makeRangeVar(snamespace, sname, -1);
	attnamelist = list_make3(makeString(snamespace),
							 makeString(cxt->relation->relname),
							 makeString(column->colname));
	altseqstmt->options = list_make1(makeDefElem("owned_by",
												 (Node *) attnamelist, -1));
	altseqstmt->for_identity = for_identity;

	if (col_exists)
		cxt->blist = lappend(cxt->blist, altseqstmt);
	else
		cxt->alist = lappend(cxt->alist, altseqstmt);

	if (snamespace_p)
		*snamespace_p = snamespace;
	if (sname_p)
		*sname_p = sname;
}


DefineSequence(ParseState *pstate, CreateSeqStmt *seq)
{
	FormData_pg_sequence seqform;
	FormData_pg_sequence_data seqdataform;
	bool		need_seq_rewrite;
	List	   *owned_by;
	CreateStmt *stmt = makeNode(CreateStmt);
	Oid			seqoid;
	ObjectAddress address;
	Relation	rel;
	HeapTuple	tuple;
	TupleDesc	tupDesc;
	Datum		value[SEQ_COL_LASTCOL];
	bool		null[SEQ_COL_LASTCOL];
	Datum		pgs_values[Natts_pg_sequence];
	bool		pgs_nulls[Natts_pg_sequence];
	int			i;

	/*
	 * If if_not_exists was given and a relation with the same name already
	 * exists, bail out. (Note: we needn't check this when not if_not_exists,
	 * because DefineRelation will complain anyway.)
	 */
	if (seq->if_not_exists)
	{
		RangeVarGetAndCheckCreationNamespace(seq->sequence, NoLock, &seqoid);
		if (OidIsValid(seqoid))
		{
			/*
			 * If we are in an extension script, insist that the pre-existing
			 * object be a member of the extension, to avoid security risks.
			 */
			ObjectAddressSet(address, RelationRelationId, seqoid);
			checkMembershipInCurrentExtension(&address);

			/* OK to skip */
			ereport(NOTICE,
					(errcode(ERRCODE_DUPLICATE_TABLE),
					 errmsg("relation \"%s\" already exists, skipping",
							seq->sequence->relname)));
			return InvalidObjectAddress;
		}
	}

	/* Check and set all option values */
	init_params(pstate, seq->options, seq->for_identity, true,
				&seqform, &seqdataform,
				&need_seq_rewrite, &owned_by);

	/*
	 * Create relation (and fill value[] and null[] for the tuple)
	 */
	stmt->tableElts = NIL;
	for (i = SEQ_COL_FIRSTCOL; i <= SEQ_COL_LASTCOL; i++)
	{
		ColumnDef  *coldef = makeNode(ColumnDef);

		coldef->inhcount = 0;
		coldef->is_local = true;
		coldef->is_not_null = true;
		coldef->is_from_type = false;
		coldef->storage = 0;
		coldef->raw_default = NULL;
		coldef->cooked_default = NULL;
		coldef->collClause = NULL;
		coldef->collOid = InvalidOid;
		coldef->constraints = NIL;
		coldef->location = -1;

		null[i - 1] = false;

		switch (i)
		{
			case SEQ_COL_LASTVAL:
				coldef->typeName = makeTypeNameFromOid(INT8OID, -1);
				coldef->colname = "last_value";
				value[i - 1] = Int64GetDatumFast(seqdataform.last_value);
				break;
			case SEQ_COL_LOG:
				coldef->typeName = makeTypeNameFromOid(INT8OID, -1);
				coldef->colname = "log_cnt";
				value[i - 1] = Int64GetDatum((int64) 0);
				break;
			case SEQ_COL_CALLED:
				coldef->typeName = makeTypeNameFromOid(BOOLOID, -1);
				coldef->colname = "is_called";
				value[i - 1] = BoolGetDatum(false);
				break;
		}
		stmt->tableElts = lappend(stmt->tableElts, coldef);
	}

	stmt->relation = seq->sequence;
	stmt->inhRelations = NIL;
	stmt->constraints = NIL;
	stmt->options = NIL;
	stmt->oncommit = ONCOMMIT_NOOP;
	stmt->tablespacename = NULL;
	stmt->if_not_exists = seq->if_not_exists;

	address = DefineRelation(stmt, RELKIND_SEQUENCE, seq->ownerId, NULL, NULL);
	seqoid = address.objectId;
	Assert(seqoid != InvalidOid);

	rel = table_open(seqoid, AccessExclusiveLock);
	tupDesc = RelationGetDescr(rel);

	/* now initialize the sequence's data */
	tuple = heap_form_tuple(tupDesc, value, null);
	fill_seq_with_data(rel, tuple);

	/* process OWNED BY if given */
	if (owned_by)
		process_owned_by(rel, owned_by, seq->for_identity);

	table_close(rel, NoLock);

	/* fill in pg_sequence */
	rel = table_open(SequenceRelationId, RowExclusiveLock);
	tupDesc = RelationGetDescr(rel);

	memset(pgs_nulls, 0, sizeof(pgs_nulls));

	pgs_values[Anum_pg_sequence_seqrelid - 1] = ObjectIdGetDatum(seqoid);
	pgs_values[Anum_pg_sequence_seqtypid - 1] = ObjectIdGetDatum(seqform.seqtypid);
	pgs_values[Anum_pg_sequence_seqstart - 1] = Int64GetDatumFast(seqform.seqstart);
	pgs_values[Anum_pg_sequence_seqincrement - 1] = Int64GetDatumFast(seqform.seqincrement);
	pgs_values[Anum_pg_sequence_seqmax - 1] = Int64GetDatumFast(seqform.seqmax);
	pgs_values[Anum_pg_sequence_seqmin - 1] = Int64GetDatumFast(seqform.seqmin);
	pgs_values[Anum_pg_sequence_seqcache - 1] = Int64GetDatumFast(seqform.seqcache);
	pgs_values[Anum_pg_sequence_seqcycle - 1] = BoolGetDatum(seqform.seqcycle);

	tuple = heap_form_tuple(tupDesc, pgs_values, pgs_nulls);
	CatalogTupleInsert(rel, tuple);

	heap_freetuple(tuple);
	table_close(rel, RowExclusiveLock);

	return address;
}


ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
				  ParamListInfo params, QueryEnvironment *queryEnv,
				  QueryCompletion *qc)
{
	Query	   *query = castNode(Query, stmt->query);
	IntoClause *into = stmt->into;
	bool		is_matview = (into->viewQuery != NULL);
	DestReceiver *dest;
	Oid			save_userid = InvalidOid;
	int			save_sec_context = 0;
	int			save_nestlevel = 0;
	ObjectAddress address;
	List	   *rewritten;
	PlannedStmt *plan;
	QueryDesc  *queryDesc;

	/* Check if the relation exists or not */
	if (CreateTableAsRelExists(stmt))
		return InvalidObjectAddress;

	/*
	 * Create the tuple receiver object and insert info it will need
	 */
	dest = CreateIntoRelDestReceiver(into);

	/*
	 * The contained Query could be a SELECT, or an EXECUTE utility command.
	 * If the latter, we just pass it off to ExecuteQuery.
	 */
	if (query->commandType == CMD_UTILITY &&
		IsA(query->utilityStmt, ExecuteStmt))
	{
		ExecuteStmt *estmt = castNode(ExecuteStmt, query->utilityStmt);

		Assert(!is_matview);	/* excluded by syntax */
		ExecuteQuery(pstate, estmt, into, params, dest, qc);

		/* get object address that intorel_startup saved for us */
		address = ((DR_intorel *) dest)->reladdr;

		return address;
	}
	Assert(query->commandType == CMD_SELECT);

	/*
	 * For materialized views, lock down security-restricted operations and
	 * arrange to make GUC variable changes local to this command.  This is
	 * not necessary for security, but this keeps the behavior similar to
	 * REFRESH MATERIALIZED VIEW.  Otherwise, one could create a materialized
	 * view not possible to refresh.
	 */
	if (is_matview)
	{
		GetUserIdAndSecContext(&save_userid, &save_sec_context);
		SetUserIdAndSecContext(save_userid,
							   save_sec_context | SECURITY_RESTRICTED_OPERATION);
		save_nestlevel = NewGUCNestLevel();
	}

	if (into->skipData)
	{
		/*
		 * If WITH NO DATA was specified, do not go through the rewriter,
		 * planner and executor.  Just define the relation using a code path
		 * similar to CREATE VIEW.  This avoids dump/restore problems stemming
		 * from running the planner before all dependencies are set up.
		 */
		address = create_ctas_nodata(query->targetList, into);
	}
	else
	{
		/*
		 * Parse analysis was done already, but we still have to run the rule
		 * rewriter.  We do not do AcquireRewriteLocks: we assume the query
		 * either came straight from the parser, or suitable locks were
		 * acquired by plancache.c.
		 */
		rewritten = QueryRewrite(query);

		/* SELECT should never rewrite to more or less than one SELECT query */
		if (list_length(rewritten) != 1)
			elog(ERROR, "unexpected rewrite result for %s",
				 is_matview ? "CREATE MATERIALIZED VIEW" :
				 "CREATE TABLE AS SELECT");
		query = linitial_node(Query, rewritten);
		Assert(query->commandType == CMD_SELECT);

		/* plan the query */
		plan = pg_plan_query(query, pstate->p_sourcetext,
							 CURSOR_OPT_PARALLEL_OK, params);

		/*
		 * Use a snapshot with an updated command ID to ensure this query sees
		 * results of any previously executed queries.  (This could only
		 * matter if the planner executed an allegedly-stable function that
		 * changed the database contents, but let's do it anyway to be
		 * parallel to the EXPLAIN code path.)
		 */
		PushCopiedSnapshot(GetActiveSnapshot());
		UpdateActiveSnapshotCommandId();

		/* Create a QueryDesc, redirecting output to our tuple receiver */
		queryDesc = CreateQueryDesc(plan, pstate->p_sourcetext,
									GetActiveSnapshot(), InvalidSnapshot,
									dest, params, queryEnv, 0);

		/* call ExecutorStart to prepare the plan for execution */
		ExecutorStart(queryDesc, GetIntoRelEFlags(into));

		/* run the plan to completion */
		ExecutorRun(queryDesc, ForwardScanDirection, 0L, true);

		/* save the rowcount if we're given a qc to fill */
		if (qc)
			SetQueryCompletion(qc, CMDTAG_SELECT, queryDesc->estate->es_processed);

		/* get object address that intorel_startup saved for us */
		address = ((DR_intorel *) dest)->reladdr;

		/* and clean up */
		ExecutorFinish(queryDesc);
		ExecutorEnd(queryDesc);

		FreeQueryDesc(queryDesc);

		PopActiveSnapshot();
	}

	if (is_matview)
	{
		/* Roll back any GUC changes */
		AtEOXact_GUC(false, save_nestlevel);

		/* Restore userid and security context */
		SetUserIdAndSecContext(save_userid, save_sec_context);
	}

	return address;
}


transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
						const char *queryString,
						List **beforeStmts, List **afterStmts)
{
	Relation	rel;
	TupleDesc	tupdesc;
	ParseState *pstate;
	CreateStmtContext cxt;
	List	   *save_alist;
	ListCell   *lcmd,
			   *l;
	List	   *newcmds = NIL;
	bool		skipValidation = true;
	AlterTableCmd *newcmd;
	ParseNamespaceItem *nsitem;

	/* Caller is responsible for locking the relation */
	rel = relation_open(relid, NoLock);
	tupdesc = RelationGetDescr(rel);

	/* Set up pstate */
	pstate = make_parsestate(NULL);
	pstate->p_sourcetext = queryString;
	nsitem = addRangeTableEntryForRelation(pstate,
										   rel,
										   AccessShareLock,
										   NULL,
										   false,
										   true);
	addNSItemToQuery(pstate, nsitem, false, true, true);

	/* Set up CreateStmtContext */
	cxt.pstate = pstate;
	if (rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
	{
		cxt.stmtType = "ALTER FOREIGN TABLE";
		cxt.isforeign = true;
	}
	else
	{
		cxt.stmtType = "ALTER TABLE";
		cxt.isforeign = false;
	}
	cxt.relation = stmt->relation;
	cxt.rel = rel;
	cxt.inhRelations = NIL;
	cxt.isalter = true;
	cxt.columns = NIL;
	cxt.ckconstraints = NIL;
	cxt.fkconstraints = NIL;
	cxt.ixconstraints = NIL;
	cxt.likeclauses = NIL;
	cxt.extstats = NIL;
	cxt.blist = NIL;
	cxt.alist = NIL;
	cxt.pkey = NULL;
	cxt.ispartitioned = (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
	cxt.partbound = NULL;
	cxt.ofType = false;

	/*
	 * Transform ALTER subcommands that need it (most don't).  These largely
	 * re-use code from CREATE TABLE.
	 */
	foreach(lcmd, stmt->cmds)
	{
		AlterTableCmd *cmd = (AlterTableCmd *) lfirst(lcmd);

		switch (cmd->subtype)
		{
			case AT_AddColumn:
			case AT_AddColumnRecurse:
				{
					ColumnDef  *def = castNode(ColumnDef, cmd->def);

					transformColumnDefinition(&cxt, def);

					/*
					 * If the column has a non-null default, we can't skip
					 * validation of foreign keys.
					 */
					if (def->raw_default != NULL)
						skipValidation = false;

					/*
					 * All constraints are processed in other ways. Remove the
					 * original list
					 */
					def->constraints = NIL;

					newcmds = lappend(newcmds, cmd);
					break;
				}

			case AT_AddConstraint:
			case AT_AddConstraintRecurse:

				/*
				 * The original AddConstraint cmd node doesn't go to newcmds
				 */
				if (IsA(cmd->def, Constraint))
				{
					transformTableConstraint(&cxt, (Constraint *) cmd->def);
					if (((Constraint *) cmd->def)->contype == CONSTR_FOREIGN)
						skipValidation = false;
				}
				else
					elog(ERROR, "unrecognized node type: %d",
						 (int) nodeTag(cmd->def));
				break;

			case AT_AlterColumnType:
				{
					ColumnDef  *def = castNode(ColumnDef, cmd->def);
					AttrNumber	attnum;

					/*
					 * For ALTER COLUMN TYPE, transform the USING clause if
					 * one was specified.
					 */
					if (def->raw_default)
					{
						def->cooked_default =
							transformExpr(pstate, def->raw_default,
										  EXPR_KIND_ALTER_COL_TRANSFORM);
					}

					/*
					 * For identity column, create ALTER SEQUENCE command to
					 * change the data type of the sequence.
					 */
					attnum = get_attnum(relid, cmd->name);
					if (attnum == InvalidAttrNumber)
						ereport(ERROR,
								(errcode(ERRCODE_UNDEFINED_COLUMN),
								 errmsg("column \"%s\" of relation \"%s\" does not exist",
										cmd->name, RelationGetRelationName(rel))));

					if (attnum > 0 &&
						TupleDescAttr(tupdesc, attnum - 1)->attidentity)
					{
						Oid			seq_relid = getIdentitySequence(relid, attnum, false);
						Oid			typeOid = typenameTypeId(pstate, def->typeName);
						AlterSeqStmt *altseqstmt = makeNode(AlterSeqStmt);

						altseqstmt->sequence = makeRangeVar(get_namespace_name(get_rel_namespace(seq_relid)),
															get_rel_name(seq_relid),
															-1);
						altseqstmt->options = list_make1(makeDefElem("as", (Node *) makeTypeNameFromOid(typeOid, -1), -1));
						altseqstmt->for_identity = true;
						cxt.blist = lappend(cxt.blist, altseqstmt);
					}

					newcmds = lappend(newcmds, cmd);
					break;
				}

			case AT_AddIdentity:
				{
					Constraint *def = castNode(Constraint, cmd->def);
					ColumnDef  *newdef = makeNode(ColumnDef);
					AttrNumber	attnum;

					newdef->colname = cmd->name;
					newdef->identity = def->generated_when;
					cmd->def = (Node *) newdef;

					attnum = get_attnum(relid, cmd->name);
					if (attnum == InvalidAttrNumber)
						ereport(ERROR,
								(errcode(ERRCODE_UNDEFINED_COLUMN),
								 errmsg("column \"%s\" of relation \"%s\" does not exist",
										cmd->name, RelationGetRelationName(rel))));

					generateSerialExtraStmts(&cxt, newdef,
											 get_atttype(relid, attnum),
											 def->options, true, true,
											 NULL, NULL);

					newcmds = lappend(newcmds, cmd);
					break;
				}

			case AT_SetIdentity:
				{
					/*
					 * Create an ALTER SEQUENCE statement for the internal
					 * sequence of the identity column.
					 */
					ListCell   *lc;
					List	   *newseqopts = NIL;
					List	   *newdef = NIL;
					AttrNumber	attnum;
					Oid			seq_relid;

					/*
					 * Split options into those handled by ALTER SEQUENCE and
					 * those for ALTER TABLE proper.
					 */
					foreach(lc, castNode(List, cmd->def))
					{
						DefElem    *def = lfirst_node(DefElem, lc);

						if (strcmp(def->defname, "generated") == 0)
							newdef = lappend(newdef, def);
						else
							newseqopts = lappend(newseqopts, def);
					}

					attnum = get_attnum(relid, cmd->name);
					if (attnum == InvalidAttrNumber)
						ereport(ERROR,
								(errcode(ERRCODE_UNDEFINED_COLUMN),
								 errmsg("column \"%s\" of relation \"%s\" does not exist",
										cmd->name, RelationGetRelationName(rel))));

					seq_relid = getIdentitySequence(relid, attnum, true);

					if (seq_relid)
					{
						AlterSeqStmt *seqstmt;

						seqstmt = makeNode(AlterSeqStmt);
						seqstmt->sequence = makeRangeVar(get_namespace_name(get_rel_namespace(seq_relid)),
														 get_rel_name(seq_relid), -1);
						seqstmt->options = newseqopts;
						seqstmt->for_identity = true;
						seqstmt->missing_ok = false;

						cxt.blist = lappend(cxt.blist, seqstmt);
					}

					/*
					 * If column was not an identity column, we just let the
					 * ALTER TABLE command error out later.  (There are cases
					 * this fails to cover, but we'll need to restructure
					 * where creation of the sequence dependency linkage
					 * happens before we can fix it.)
					 */

					cmd->def = (Node *) newdef;
					newcmds = lappend(newcmds, cmd);
					break;
				}

			case AT_AttachPartition:
			case AT_DetachPartition:
				{
					PartitionCmd *partcmd = (PartitionCmd *) cmd->def;

					transformPartitionCmd(&cxt, partcmd);
					/* assign transformed value of the partition bound */
					partcmd->bound = cxt.partbound;
				}

				newcmds = lappend(newcmds, cmd);
				break;

			default:

				/*
				 * Currently, we shouldn't actually get here for subcommand
				 * types that don't require transformation; but if we do, just
				 * emit them unchanged.
				 */
				newcmds = lappend(newcmds, cmd);
				break;
		}
	}

	/*
	 * Transfer anything we already have in cxt.alist into save_alist, to keep
	 * it separate from the output of transformIndexConstraints.
	 */
	save_alist = cxt.alist;
	cxt.alist = NIL;

	/* Postprocess constraints */
	transformIndexConstraints(&cxt);
	transformFKConstraints(&cxt, skipValidation, true);
	transformCheckConstraints(&cxt, false);

	/*
	 * Push any index-creation commands into the ALTER, so that they can be
	 * scheduled nicely by tablecmds.c.  Note that tablecmds.c assumes that
	 * the IndexStmt attached to an AT_AddIndex or AT_AddIndexConstraint
	 * subcommand has already been through transformIndexStmt.
	 */
	foreach(l, cxt.alist)
	{
		Node	   *istmt = (Node *) lfirst(l);

		/*
		 * We assume here that cxt.alist contains only IndexStmts and possibly
		 * ALTER TABLE SET NOT NULL statements generated from primary key
		 * constraints.  We absorb the subcommands of the latter directly.
		 */
		if (IsA(istmt, IndexStmt))
		{
			IndexStmt  *idxstmt = (IndexStmt *) istmt;

			idxstmt = transformIndexStmt(relid, idxstmt, queryString);
			newcmd = makeNode(AlterTableCmd);
			newcmd->subtype = OidIsValid(idxstmt->indexOid) ? AT_AddIndexConstraint : AT_AddIndex;
			newcmd->def = (Node *) idxstmt;
			newcmds = lappend(newcmds, newcmd);
		}
		else if (IsA(istmt, AlterTableStmt))
		{
			AlterTableStmt *alterstmt = (AlterTableStmt *) istmt;

			newcmds = list_concat(newcmds, alterstmt->cmds);
		}
		else
			elog(ERROR, "unexpected stmt type %d", (int) nodeTag(istmt));
	}
	cxt.alist = NIL;

	/* Append any CHECK or FK constraints to the commands list */
	foreach(l, cxt.ckconstraints)
	{
		newcmd = makeNode(AlterTableCmd);
		newcmd->subtype = AT_AddConstraint;
		newcmd->def = (Node *) lfirst(l);
		newcmds = lappend(newcmds, newcmd);
	}
	foreach(l, cxt.fkconstraints)
	{
		newcmd = makeNode(AlterTableCmd);
		newcmd->subtype = AT_AddConstraint;
		newcmd->def = (Node *) lfirst(l);
		newcmds = lappend(newcmds, newcmd);
	}

	/* Append extended statistics objects */
	transformExtendedStatistics(&cxt);

	/* Close rel */
	relation_close(rel, NoLock);

	/*
	 * Output results.
	 */
	stmt->cmds = newcmds;

	*beforeStmts = cxt.blist;
	*afterStmts = list_concat(cxt.alist, save_alist);

	return stmt;
}


AlterSchemaOwner_internal(HeapTuple tup, Relation rel, Oid newOwnerId)
{
	Form_pg_namespace nspForm;

	Assert(tup->t_tableOid == NamespaceRelationId);
	Assert(RelationGetRelid(rel) == NamespaceRelationId);

	nspForm = (Form_pg_namespace) GETSTRUCT(tup);

	/*
	 * If the new owner is the same as the existing owner, consider the
	 * command to have succeeded.  This is for dump restoration purposes.
	 */
	if (nspForm->nspowner != newOwnerId)
	{
		Datum		repl_val[Natts_pg_namespace];
		bool		repl_null[Natts_pg_namespace];
		bool		repl_repl[Natts_pg_namespace];
		Acl		   *newAcl;
		Datum		aclDatum;
		bool		isNull;
		HeapTuple	newtuple;
		AclResult	aclresult;

		/* Otherwise, must be owner of the existing object */
		if (!pg_namespace_ownercheck(nspForm->oid, GetUserId()))
			aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_SCHEMA,
						   NameStr(nspForm->nspname));

		/* Must be able to become new owner */
		check_is_member_of_role(GetUserId(), newOwnerId);

		/*
		 * must have create-schema rights
		 *
		 * NOTE: This is different from other alter-owner checks in that the
		 * current user is checked for create privileges instead of the
		 * destination owner.  This is consistent with the CREATE case for
		 * schemas.  Because superusers will always have this right, we need
		 * no special case for them.
		 */
		aclresult = pg_database_aclcheck(MyDatabaseId, GetUserId(),
										 ACL_CREATE);
		if (aclresult != ACLCHECK_OK)
			aclcheck_error(aclresult, OBJECT_DATABASE,
						   get_database_name(MyDatabaseId));

		memset(repl_null, false, sizeof(repl_null));
		memset(repl_repl, false, sizeof(repl_repl));

		repl_repl[Anum_pg_namespace_nspowner - 1] = true;
		repl_val[Anum_pg_namespace_nspowner - 1] = ObjectIdGetDatum(newOwnerId);

		/*
		 * Determine the modified ACL for the new owner.  This is only
		 * necessary when the ACL is non-null.
		 */
		aclDatum = SysCacheGetAttr(NAMESPACENAME, tup,
								   Anum_pg_namespace_nspacl,
								   &isNull);
		if (!isNull)
		{
			newAcl = aclnewowner(DatumGetAclP(aclDatum),
								 nspForm->nspowner, newOwnerId);
			repl_repl[Anum_pg_namespace_nspacl - 1] = true;
			repl_val[Anum_pg_namespace_nspacl - 1] = PointerGetDatum(newAcl);
		}

		newtuple = heap_modify_tuple(tup, RelationGetDescr(rel), repl_val, repl_null, repl_repl);

		CatalogTupleUpdate(rel, &newtuple->t_self, newtuple);

		heap_freetuple(newtuple);

		/* Update owner dependency reference */
		changeDependencyOnOwner(NamespaceRelationId, nspForm->oid,
								newOwnerId);
	}

	InvokeObjectPostAlterHook(NamespaceRelationId,
							  nspForm->oid, 0);
}


size_t Curl_conncache_size(struct Curl_easy *data)
{
  size_t num;
  CONNCACHE_LOCK(data);
  num = data->state.conn_cache->num_conn;
  CONNCACHE_UNLOCK(data);
  return num;
}


CURLcode Curl_conncache_add_conn(struct Curl_easy *data)
{
  CURLcode result = CURLE_OK;
  struct connectbundle *bundle = NULL;
  struct connectdata *conn = data->conn;
  struct conncache *connc = data->state.conn_cache;
  DEBUGASSERT(conn);

  /* *find_bundle() locks the connection cache */
  bundle = Curl_conncache_find_bundle(data, conn, data->state.conn_cache,
                                      NULL);
  if(!bundle) {
    char key[HASHKEY_SIZE];

    result = bundle_create(&bundle);
    if(result) {
      goto unlock;
    }

    hashkey(conn, key, sizeof(key), NULL);

    if(!conncache_add_bundle(data->state.conn_cache, key, bundle)) {
      bundle_destroy(bundle);
      result = CURLE_OUT_OF_MEMORY;
      goto unlock;
    }
  }

  bundle_add_conn(bundle, conn);
  conn->connection_id = connc->next_connection_id++;
  connc->num_conn++;

  DEBUGF(infof(data, "Added connection %ld. "
               "The cache now contains %zu members",
               conn->connection_id, connc->num_conn));

  unlock:
  CONNCACHE_UNLOCK(data);

  return result;
}


void callbacks_update_scrollbar_positions (void){
	gdouble positionX,positionY;

	positionX = screenRenderInfo.lowerLeftX;
	if (positionX < ((GtkAdjustment *)screen.win.hAdjustment)->lower)
		positionX = ((GtkAdjustment *)screen.win.hAdjustment)->lower;
	if (positionX > (((GtkAdjustment *)screen.win.hAdjustment)->upper - ((GtkAdjustment *)screen.win.hAdjustment)->page_size))
		positionX = (((GtkAdjustment *)screen.win.hAdjustment)->upper - ((GtkAdjustment *)screen.win.hAdjustment)->page_size);
	gtk_adjustment_set_value ((GtkAdjustment *)screen.win.hAdjustment, positionX);

	positionY = ((GtkAdjustment *)screen.win.vAdjustment)->upper - screenRenderInfo.lowerLeftY -
		((GtkAdjustment *)screen.win.vAdjustment)->page_size +
		((GtkAdjustment *)screen.win.vAdjustment)->lower;
	if (positionY < ((GtkAdjustment *)screen.win.vAdjustment)->lower)
		positionY = ((GtkAdjustment *)screen.win.vAdjustment)->lower;
	if (positionY > (((GtkAdjustment *)screen.win.vAdjustment)->upper - ((GtkAdjustment *)screen.win.vAdjustment)->page_size))
		positionY = (((GtkAdjustment *)screen.win.vAdjustment)->upper - ((GtkAdjustment *)screen.win.vAdjustment)->page_size);
	gtk_adjustment_set_value ((GtkAdjustment *)screen.win.vAdjustment, positionY);
}


callbacks_drawingarea_button_release_event (GtkWidget *widget, GdkEventButton *event)
{
	gint index;

	if (event->type != GDK_BUTTON_RELEASE)
		return TRUE;

	switch (screen.state) {
	case IN_MOVE:
		screen.off_x = 0;
		screen.off_y = 0;
		render_refresh_rendered_image_on_screen ();
		callbacks_switch_to_normal_tool_cursor (screen.tool);
		break;

	case IN_ZOOM_OUTLINE:
		if ((event->state & GDK_SHIFT_MASK) != 0) {
			render_zoom_display (ZOOM_OUT_CMOUSE, 0,
					event->x, event->y);
		}
		/* if the user just clicks without dragging, then simply
		   zoom in a preset amount */
		else if ((abs(screen.start_x - event->x) < 4) &&
				(abs(screen.start_y - event->y) < 4)) {
			render_zoom_display (ZOOM_IN_CMOUSE, 0,
					event->x, event->y);
		} else {
			render_calculate_zoom_from_outline (widget, event);
		}
		callbacks_switch_to_normal_tool_cursor (screen.tool);
		break;

	case IN_SELECTION_DRAG:
		/* selection will only work with cairo, so do nothing if it's
		   not compiled */
		index = callbacks_get_selected_row_index ();

		if ((index >= 0) && mainProject->file[index]->isVisible) {
			enum selection_action sel_action = SELECTION_REPLACE;

			if (event->state & GDK_SHIFT_MASK)
				sel_action = SELECTION_ADD;
			else if (event->state & GDK_CONTROL_MASK)
				sel_action = SELECTION_TOGGLE;

			/* determine if this was just a click or a box drag */
			if ((fabs((double)(screen.last_x - screen.start_x)) < 5)
			 && (fabs((double)(screen.last_y - screen.start_y)) < 5)) {
				render_fill_selection_buffer_from_mouse_click (
						event->x, event->y, index,
						sel_action);
			} else {
				render_fill_selection_buffer_from_mouse_drag (
						event->x, event->y,
						screen.start_x, screen.start_y,
						index, sel_action);
			}

			/* Check if anything was selected */
			update_selected_object_message (TRUE);

			check_align_files_possibility (&screen.selectionInfo);
		} else {
			render_refresh_rendered_image_on_screen ();
		}
		break;
	default:
		break;
	}

	screen.state = NORMAL;

	return TRUE;
} /* button_release_event */


callbacks_move_layer_down_button_clicked  (GtkButton *button, gpointer   user_data) {
	gint index=callbacks_get_selected_row_index();
	if (index < 0) {
		show_no_layers_warning ();
		return;
	}

	if (index < mainProject->last_loaded) {
		gerbv_change_layer_order (mainProject, index, index + 1);
		callbacks_update_layer_tree ();
		callbacks_select_layer_row (index + 1);

		if (screenRenderInfo.renderType <= GERBV_RENDER_TYPE_GDK_XOR) {
			render_refresh_rendered_image_on_screen ();
		}
		else {
			render_recreate_composite_surface (screen.drawing_area);
			callbacks_force_expose_event_for_screen ();
		}
	}
}


static void parea_report (gerbv_net_t *net,
		gerbv_image_t *img, gerbv_project_t *prj)
{
	gerbv_net_t *n;
	unsigned int c = 0;
	gerbv_interpolation_t inter_prev;
	double x, y;

	if (net->interpolation != GERBV_INTERPOLATION_PAREA_START)
		return;

	/* Count vertices */
	for (gerbv_net_t *n = net->next; n != NULL; n = n->next) {
		if (n->interpolation == GERBV_INTERPOLATION_PAREA_END)
			break;
		c++;
	}

	g_message (_("    Number of vertices: %u"), c - 1);

	for (n = net->next, inter_prev = net->interpolation;
			n != NULL
			&& n->interpolation != GERBV_INTERPOLATION_PAREA_END;
			n = n->next) {

		switch (n->interpolation) {

		case GERBV_INTERPOLATION_LINEARx1:

			if (inter_prev != n->interpolation) {
				x = n->start_x;
				y = n->start_y;
				gerbv_transform_coord_for_image(&x, &y,
						img, prj);
				g_message (_("    Line from: (%g, %g) %s"),
						screen_units(x),
						screen_units(y),
						screen_units_str());
			}

			x = n->stop_x;
			y = n->stop_y;
			gerbv_transform_coord_for_image(&x, &y, img, prj);
			g_message (_("        Line to: (%g, %g) %s"),
					screen_units(x), screen_units(y),
					screen_units_str());
			break;

		case GERBV_INTERPOLATION_CW_CIRCULAR:
		case GERBV_INTERPOLATION_CCW_CIRCULAR:

			x = n->start_x;
			y = n->start_y;
			gerbv_transform_coord_for_image(&x, &y, img, prj);
			g_message (_("    Arc from: (%g, %g) %s"),
					screen_units(x), screen_units(y),
					screen_units_str());

			x = n->stop_x;
			y = n->stop_y;
			gerbv_transform_coord_for_image(&x, &y, img, prj);
			g_message (_("        Arc to: (%g, %g) %s"),
					screen_units(x), screen_units(y),
					screen_units_str());

			x = n->cirseg->cp_x;
			y = n->cirseg->cp_y;
			gerbv_transform_coord_for_image(&x, &y, img, prj);
			g_message (_("        Center: (%g, %g) %s"),
					screen_units(x), screen_units(y),
					screen_units_str());

			x = n->cirseg->width;
			y = n->cirseg->height;
			gerbv_transform_coord_for_image(&x, &y, img, prj);
			g_message (_("        Radius: %g %s"),
					screen_units(x)/2, screen_units_str());

			g_message (_("        Angle: %g deg"),
				fabs(n->cirseg->angle1 - n->cirseg->angle2));
			g_message (_("        Angles: (%g, %g) deg"),
					n->cirseg->angle1, n->cirseg->angle2);
			g_message (_("        Direction: %s"),
					(n->interpolation ==
					 GERBV_INTERPOLATION_CW_CIRCULAR)?
						_("CW"): _("CCW"));
			break;

		default:
			g_message("       Skipping interpolation: %s",
				_(gerbv_interpolation_name(n->interpolation)));
		}

		inter_prev = n->interpolation;
	}
}


callbacks_window_key_release_event (GtkWidget *widget, GdkEventKey *event)
{
	return TRUE;
} /* key_release_event */


callbacks_fullscreen_toggled (GtkMenuItem *menuitem, gpointer user_data)
{
	//struct GtkWindow *win = (struct GtkWindow *)(screen.win.topLevelWindow);
	GdkWindowState state = gdk_window_get_state (gtk_widget_get_window(screen.win.topLevelWindow));
	if(state & GDK_WINDOW_STATE_FULLSCREEN)
		gtk_window_unfullscreen (GTK_WINDOW(screen.win.topLevelWindow));
	else
		gtk_window_fullscreen (GTK_WINDOW(screen.win.topLevelWindow));
}


void open_files(GSList *filenames)
{
	GSList *fns = NULL;		/* File names to ask */
	GSList *fns_is_mod = NULL;	/* File name layer is modified */
	GSList *fns_cnt = NULL;		/* File names count */
	GSList *fns_lay_num = NULL;	/* Layer number for fns */
	GSList *cnt = NULL;		/* File names count unsorted by layers,
					   0 -- file not yet loaded as layer */
	gint answer;

	if (filenames == NULL)
		return;

	/* Check if there is a Gerbv project in the list.
	 * If there is least open only that and ignore the rest. */
	for (GSList *fn = filenames; fn; fn = fn->next) {
		gboolean is_project = FALSE;
		if (0 == project_is_gerbv_project(fn->data, &is_project)
		&&  is_project) {
			open_project(fn->data);

			gerbv_render_zoom_to_fit_display(mainProject,
					&screenRenderInfo);
			render_refresh_rendered_image_on_screen();
			callbacks_update_layer_tree();

			return;
		}
	}

	/* Count opened filenames and place result in list */
	for (GSList *fn = filenames; fn; fn = fn->next) {
		gint c = 0;

		for (gint fidx = 0; fidx <= mainProject->last_loaded; ++fidx) {
			gchar *fpn = mainProject->file[fidx]->fullPathname;

			if (strlen(fpn) == strlen(fn->data)
			&&  0 == g_ascii_strncasecmp(fpn, fn->data,
						strlen(fn->data))) {
				c++;
			}
		}

		cnt = g_slist_append(cnt, GINT_TO_POINTER(c));
	}

	/* Make fns, fns_is_mod and fns_cnt lists sorted by layers */
	for (gint fidx = 0; fidx <= mainProject->last_loaded; ++fidx) {
		gchar *fpn = mainProject->file[fidx]->fullPathname;

		for (GSList *fn = filenames; fn; fn = fn->next) {
			if (strlen(fpn) == strlen(fn->data)
			&&  0 == g_ascii_strncasecmp(fpn, fn->data,
					strlen(fn->data))) {
				fns = g_slist_append(fns, fn->data);
				fns_is_mod = g_slist_append(fns_is_mod,
						GINT_TO_POINTER(mainProject->
							file[fidx]->
							layer_dirty));
				fns_cnt = g_slist_append(fns_cnt,
						g_slist_nth_data(cnt,
							g_slist_position(
								filenames,
								fn)));
				fns_lay_num = g_slist_append(fns_lay_num,
						GINT_TO_POINTER(fidx));

				break;
			}
		}
	}

	answer = GTK_RESPONSE_NONE;
	if (g_slist_length(fns) > 0)
		answer = interface_reopen_question(fns, fns_is_mod,
							fns_cnt, fns_lay_num);

	switch (answer) {

	case GTK_RESPONSE_CANCEL:
	case GTK_RESPONSE_NONE:
	case GTK_RESPONSE_DELETE_EVENT:
		/* Dialog is closed or Esc is pressed, skip all */
		break;

	case GTK_RESPONSE_YES: /* Reload layer was selected */
		for (GSList *fn = fns; fn; fn = fn->next) {
			if (fn->data != NULL)
				gerbv_revert_file(mainProject,
					GPOINTER_TO_INT(
						g_slist_nth_data (fns_lay_num,
							g_slist_position (fns,
									fn))));
		}
		break;

	case GTK_RESPONSE_OK: /* Open as a new layer was selected */
		/* To open as new only _one_ instance of file, check filenames
		 * by selected files in fns */
		for (GSList *fn = filenames; fn; fn = fn->next) {
			if (NULL != g_slist_find (fns, fn->data))
				gerbv_open_layer_from_filename(mainProject,
								fn->data);
		}
		break;
	}

	/* Add not loaded files (cnt == 0) in the end */
	for (GSList *fn = filenames; fn; fn = fn->next) {
		if (0 == GPOINTER_TO_INT(g_slist_nth_data(cnt,
					g_slist_position(filenames, fn))))
			gerbv_open_layer_from_filename (mainProject, fn->data);
	}

	g_slist_free(fns);
	g_slist_free(fns_is_mod);
	g_slist_free(fns_cnt);
	g_slist_free(fns_lay_num);
	g_slist_free(cnt);

	gerbv_render_zoom_to_fit_display (mainProject, &screenRenderInfo);
	render_refresh_rendered_image_on_screen();
	callbacks_update_layer_tree();
}


static const char *error_type_string(gerbv_message_type_t type) {
	switch (type) {
	case GERBV_MESSAGE_FATAL:
		return _("FATAL");
	case GERBV_MESSAGE_ERROR:
		return _("ERROR");
	case GERBV_MESSAGE_WARNING:
		return _("Warning");
	case GERBV_MESSAGE_NOTE:
		return _("Note");
	default:
		return "Unknown";
	}
}


callbacks_show_selection_on_invisible (GtkMenuItem *menuitem, gpointer user_data)
{
	mainProject->show_invisible_selection = GTK_CHECK_MENU_ITEM(menuitem)->active;
	render_refresh_rendered_image_on_screen();
}


callbacks_window_scroll_event(GtkWidget *widget, GdkEventScroll *event)
{
	switch (event->direction) {
		case GDK_SCROLL_UP:
			render_zoom_display (ZOOM_IN_CMOUSE, 0, event->x, event->y);
			break;
		case GDK_SCROLL_DOWN:
			render_zoom_display (ZOOM_OUT_CMOUSE, 0, event->x, event->y);
			break;
		case GDK_SCROLL_LEFT:
			/* Ignore */
		case GDK_SCROLL_RIGHT:
			/* Ignore */
		default:
			return TRUE;
	}
	return TRUE;
} /* scroll_event */


callbacks_display_object_properties_clicked (GtkButton *button, gpointer user_data)
{
	gint index = callbacks_get_selected_row_index ();
	guint i;

	if (index < 0 || selection_length (&screen.selectionInfo) == 0) {
		interface_show_alert_dialog(_("No object is currently selected"),
			_("Objects must be selected using the pointer tool "
			"before you can view the object properties."),
			FALSE, NULL);
		return;
	}

	for (i = 0; i < selection_length (&screen.selectionInfo); i++){
		gerbv_selection_item_t sItem =
			selection_get_item_by_index (&screen.selectionInfo, i);

		gerbv_net_t *net = sItem.net;
		gerbv_image_t *image = sItem.image;

		if (net->interpolation == GERBV_INTERPOLATION_PAREA_START) {
			/* Spacing for a pretty display */
			if (i != 0)
				g_message (" ");

			g_message (_("Object type: Polygon"));
			parea_report (net, image, mainProject);
			net_layer_file_report (net, image, mainProject);
		} else {
			switch (net->aperture_state) {

			case GERBV_APERTURE_STATE_ON:
			case GERBV_APERTURE_STATE_FLASH:
				/* Spacing for a pretty display */
				if (i != 0)
					g_message (" ");
				break;

			default:
				break;
			}

			aperture_state_report (net, image, mainProject);
		}
	}
	/* Use separator for different report requests */
	g_message ("---------------------------------------");
}


callbacks_save_layer_activate                       (GtkMenuItem     *menuitem,
                                        gpointer         user_data)
{
  /* first figure out which layer in the layer side menu is selected */
  gint index=callbacks_get_selected_row_index();

  /* Now save that layer */
  if (index >= 0) {
    if (!gerbv_save_layer_from_index (mainProject, index, mainProject->file[index]->fullPathname)) {
      interface_show_alert_dialog(_("Gerbv cannot export this file type"),
				  NULL,
				  FALSE,
				  NULL);
      mainProject->file[index]->layer_dirty = FALSE;
      callbacks_update_layer_tree();
      return;
    }
  }
  callbacks_update_layer_tree();
  return;
}


void utf8_snprintf(gchar *dst, gsize byte_len, const gchar *fmt, ...)
{
	va_list ap;

	va_start(ap, fmt);
	gchar *str = g_strdup_vprintf(fmt, ap);
	va_end(ap);
	utf8_strncpy(dst, str, byte_len);
	g_free(str);
}


static void free_tables(H264Context *h, int free_rbsp)
{
    int i;
    H264Context *hx;

    av_freep(&h->intra4x4_pred_mode);
    av_freep(&h->chroma_pred_mode_table);
    av_freep(&h->cbp_table);
    av_freep(&h->mvd_table[0]);
    av_freep(&h->mvd_table[1]);
    av_freep(&h->direct_table);
    av_freep(&h->non_zero_count);
    av_freep(&h->slice_table_base);
    h->slice_table = NULL;
    av_freep(&h->list_counts);

    av_freep(&h->mb2b_xy);
    av_freep(&h->mb2br_xy);

    av_buffer_pool_uninit(&h->qscale_table_pool);
    av_buffer_pool_uninit(&h->mb_type_pool);
    av_buffer_pool_uninit(&h->motion_val_pool);
    av_buffer_pool_uninit(&h->ref_index_pool);

    if (free_rbsp && h->DPB) {
        for (i = 0; i < MAX_PICTURE_COUNT; i++)
            unref_picture(h, &h->DPB[i]);
        av_freep(&h->DPB);
    } else if (h->DPB) {
        for (i = 0; i < MAX_PICTURE_COUNT; i++)
            h->DPB[i].needs_realloc = 1;
    }

    h->cur_pic_ptr = NULL;

    for (i = 0; i < MAX_THREADS; i++) {
        hx = h->thread_context[i];
        if (!hx)
            continue;
        av_freep(&hx->top_borders[1]);
        av_freep(&hx->top_borders[0]);
        av_freep(&hx->bipred_scratchpad);
        av_freep(&hx->edge_emu_buffer);
        av_freep(&hx->dc_val_base);
        av_freep(&hx->me.scratchpad);
        av_freep(&hx->er.mb_index2xy);
        av_freep(&hx->er.error_status_table);
        av_freep(&hx->er.er_temp_buffer);
        av_freep(&hx->er.mbintra_table);
        av_freep(&hx->er.mbskip_table);

        if (free_rbsp) {
            av_freep(&hx->rbsp_buffer[1]);
            av_freep(&hx->rbsp_buffer[0]);
            hx->rbsp_buffer_size[0] = 0;
            hx->rbsp_buffer_size[1] = 0;
        }
        if (i)
            av_freep(&h->thread_context[i]);
    }
}