void CLASS parse_phase_one(int base)
{
  unsigned entries, tag, type, len, data, save, i, c;
  float romm_cam[3][3];
  char *cp;

  memset(&ph1, 0, sizeof ph1);
  fseek(ifp, base, SEEK_SET);
  order = get4() & 0xffff;
  if (get4() >> 8 != 0x526177)
    return; /* "Raw" */
  fseek(ifp, get4() + base, SEEK_SET);
  entries = get4();
  get4();
  while (entries--)
  {
    tag = get4();
    type = get4();
    len = get4();
    data = get4();
    save = ftell(ifp);
    fseek(ifp, base + data, SEEK_SET);
    switch (tag)
    {

#ifdef LIBRAW_LIBRARY_BUILD
    case 0x0102:
      stmread(imgdata.shootinginfo.BodySerial, len, ifp);
      if ((imgdata.shootinginfo.BodySerial[0] == 0x4c) && (imgdata.shootinginfo.BodySerial[1] == 0x49))
      {
        unique_id =
            (((imgdata.shootinginfo.BodySerial[0] & 0x3f) << 5) | (imgdata.shootinginfo.BodySerial[2] & 0x3f)) - 0x41;
      }
      else
      {
        unique_id =
            (((imgdata.shootinginfo.BodySerial[0] & 0x3f) << 5) | (imgdata.shootinginfo.BodySerial[1] & 0x3f)) - 0x41;
      }
      setPhaseOneFeatures(unique_id);
      break;
    case 0x0401:
      if (type == 4)
        imgdata.lens.makernotes.CurAp = powf64(2.0f, (int_to_float(data) / 2.0f));
      else
        imgdata.lens.makernotes.CurAp = powf64(2.0f, (getreal(type) / 2.0f));
      break;
    case 0x0403:
      if (type == 4)
        imgdata.lens.makernotes.CurFocal = int_to_float(data);
      else
        imgdata.lens.makernotes.CurFocal = getreal(type);
      break;
    case 0x0410:
      stmread(imgdata.lens.makernotes.body, len, ifp);
      break;
    case 0x0412:
      stmread(imgdata.lens.makernotes.Lens, len, ifp);
      break;
    case 0x0414:
      if (type == 4)
      {
        imgdata.lens.makernotes.MaxAp4CurFocal = powf64(2.0f, (int_to_float(data) / 2.0f));
      }
      else
      {
        imgdata.lens.makernotes.MaxAp4CurFocal = powf64(2.0f, (getreal(type) / 2.0f));
      }
      break;
    case 0x0415:
      if (type == 4)
      {
        imgdata.lens.makernotes.MinAp4CurFocal = powf64(2.0f, (int_to_float(data) / 2.0f));
      }
      else
      {
        imgdata.lens.makernotes.MinAp4CurFocal = powf64(2.0f, (getreal(type) / 2.0f));
      }
      break;
    case 0x0416:
      if (type == 4)
      {
        imgdata.lens.makernotes.MinFocal = int_to_float(data);
      }
      else
      {
        imgdata.lens.makernotes.MinFocal = getreal(type);
      }
      if (imgdata.lens.makernotes.MinFocal > 1000.0f)
      {
        imgdata.lens.makernotes.MinFocal = 0.0f;
      }
      break;
    case 0x0417:
      if (type == 4)
      {
        imgdata.lens.makernotes.MaxFocal = int_to_float(data);
      }
      else
      {
        imgdata.lens.makernotes.MaxFocal = getreal(type);
      }
      break;
#endif

    case 0x100:
      flip = "0653"[data & 3] - '0';
      break;
    case 0x106:
      for (i = 0; i < 9; i++)
#ifdef LIBRAW_LIBRARY_BUILD
        imgdata.color.P1_color[0].romm_cam[i] =
#endif
            ((float *)romm_cam)[i] = getreal(11);
      romm_coeff(romm_cam);
      break;
    case 0x107:
      FORC3 cam_mul[c] = getreal(11);
      break;
    case 0x108:
      raw_width = data;
      break;
    case 0x109:
      raw_height = data;
      break;
    case 0x10a:
      left_margin = data;
      break;
    case 0x10b:
      top_margin = data;
      break;
    case 0x10c:
      width = data;
      break;
    case 0x10d:
      height = data;
      break;
    case 0x10e:
      ph1.format = data;
      break;
    case 0x10f:
      data_offset = data + base;
      break;
    case 0x110:
      meta_offset = data + base;
      meta_length = len;
      break;
    case 0x112:
      ph1.key_off = save - 4;
      break;
    case 0x210:
      ph1.tag_210 = int_to_float(data);
      break;
    case 0x21a:
      ph1.tag_21a = data;
      break;
    case 0x21c:
      strip_offset = data + base;
      break;
    case 0x21d:
      ph1.t_black = data;
      break;
    case 0x222:
      ph1.split_col = data;
      break;
    case 0x223:
      ph1.black_col = data + base;
      break;
    case 0x224:
      ph1.split_row = data;
      break;
    case 0x225:
      ph1.black_row = data + base;
      break;
#ifdef LIBRAW_LIBRARY_BUILD
    case 0x226:
      for (i = 0; i < 9; i++)
        imgdata.color.P1_color[1].romm_cam[i] = getreal(11);
      break;
#endif
    case 0x301:
      model[63] = 0;
      fread(model, 1, 63, ifp);
      if ((cp = strstr(model, " camera")))
        *cp = 0;
    }
    fseek(ifp, save, SEEK_SET);
  }

#ifdef LIBRAW_LIBRARY_BUILD
  if (!imgdata.lens.makernotes.body[0] && !imgdata.shootinginfo.BodySerial[0])
  {
    fseek(ifp, meta_offset, SEEK_SET);
    order = get2();
    fseek(ifp, 6, SEEK_CUR);
    fseek(ifp, meta_offset + get4(), SEEK_SET);
    entries = get4();
    get4();
    while (entries--)
    {
      tag = get4();
      len = get4();
      data = get4();
      save = ftell(ifp);
      fseek(ifp, meta_offset + data, SEEK_SET);
      if (tag == 0x0407)
      {
        stmread(imgdata.shootinginfo.BodySerial, len, ifp);
        if ((imgdata.shootinginfo.BodySerial[0] == 0x4c) && (imgdata.shootinginfo.BodySerial[1] == 0x49))
        {
          unique_id =
              (((imgdata.shootinginfo.BodySerial[0] & 0x3f) << 5) | (imgdata.shootinginfo.BodySerial[2] & 0x3f)) - 0x41;
        }
        else
        {
          unique_id =
              (((imgdata.shootinginfo.BodySerial[0] & 0x3f) << 5) | (imgdata.shootinginfo.BodySerial[1] & 0x3f)) - 0x41;
        }
        setPhaseOneFeatures(unique_id);
      }
      fseek(ifp, save, SEEK_SET);
    }
  }
#endif

  load_raw = ph1.format < 3 ? &CLASS phase_one_load_raw : &CLASS phase_one_load_raw_c;
  maximum = 0xffff;
  strcpy(make, "Phase One");
  if (model[0])
    return;
  switch (raw_height)
  {
  case 2060:
    strcpy(model, "LightPhase");
    break;
  case 2682:
    strcpy(model, "H 10");
    break;
  case 4128:
    strcpy(model, "H 20");
    break;
  case 5488:
    strcpy(model, "H 25");
    break;
  }
}


void CLASS parseSonyLensType2(uchar a, uchar b)
{
  ushort lid2;
  lid2 = (((ushort)a) << 8) | ((ushort)b);
  if (!lid2)
    return;
  if (lid2 < 0x100)
  {
    if ((imgdata.lens.makernotes.AdapterID != 0x4900) && (imgdata.lens.makernotes.AdapterID != 0xEF00))
    {
      imgdata.lens.makernotes.AdapterID = lid2;
      switch (lid2)
      {
      case 1:
      case 2:
      case 3:
      case 6:
        imgdata.lens.makernotes.LensMount = LIBRAW_MOUNT_Minolta_A;
        break;
      case 44:
      case 78:
      case 239:
        imgdata.lens.makernotes.LensMount = LIBRAW_MOUNT_Canon_EF;
        break;
      }
    }
  }
  else
    imgdata.lens.makernotes.LensID = lid2;
  if ((lid2 >= 50481) && (lid2 < 50500))
  {
    strcpy(imgdata.lens.makernotes.Adapter, "MC-11");
    imgdata.lens.makernotes.AdapterID = 0x4900;
  }
  return;
}


void CLASS parse_minolta(int base)
{
  int save, tag, len, offset, high = 0, wide = 0, i, c;
  short sorder = order;

  fseek(ifp, base, SEEK_SET);
  if (fgetc(ifp) || fgetc(ifp) - 'M' || fgetc(ifp) - 'R')
    return;
  order = fgetc(ifp) * 0x101;
  offset = base + get4() + 8;
  while ((save = ftell(ifp)) < offset)
  {
    for (tag = i = 0; i < 4; i++)
      tag = tag << 8 | fgetc(ifp);
    len = get4();
    switch (tag)
    {
    case 0x505244: /* PRD */
      fseek(ifp, 8, SEEK_CUR);
      high = get2();
      wide = get2();
      break;
#ifdef LIBRAW_LIBRARY_BUILD
    case 0x524946: /* RIF */
      if (!strncasecmp(model, "DSLR-A100", 9))
      {
        fseek(ifp, 8, SEEK_CUR);
        imgdata.color.WB_Coeffs[LIBRAW_WBI_Tungsten][0] = get2();
        imgdata.color.WB_Coeffs[LIBRAW_WBI_Tungsten][2] = get2();
        imgdata.color.WB_Coeffs[LIBRAW_WBI_Daylight][0] = get2();
        imgdata.color.WB_Coeffs[LIBRAW_WBI_Daylight][2] = get2();
        imgdata.color.WB_Coeffs[LIBRAW_WBI_Cloudy][0] = get2();
        imgdata.color.WB_Coeffs[LIBRAW_WBI_Cloudy][2] = get2();
        imgdata.color.WB_Coeffs[LIBRAW_WBI_FL_W][0] = get2();
        imgdata.color.WB_Coeffs[LIBRAW_WBI_FL_W][2] = get2();
        imgdata.color.WB_Coeffs[LIBRAW_WBI_Flash][0] = get2();
        imgdata.color.WB_Coeffs[LIBRAW_WBI_Flash][2] = get2();
        get4();
        imgdata.color.WB_Coeffs[LIBRAW_WBI_Shade][0] = get2();
        imgdata.color.WB_Coeffs[LIBRAW_WBI_Shade][2] = get2();
        imgdata.color.WB_Coeffs[LIBRAW_WBI_FL_D][0] = get2();
        imgdata.color.WB_Coeffs[LIBRAW_WBI_FL_D][2] = get2();
        imgdata.color.WB_Coeffs[LIBRAW_WBI_FL_N][0] = get2();
        imgdata.color.WB_Coeffs[LIBRAW_WBI_FL_N][2] = get2();
        imgdata.color.WB_Coeffs[LIBRAW_WBI_FL_WW][0] = get2();
        imgdata.color.WB_Coeffs[LIBRAW_WBI_FL_WW][2] = get2();
        imgdata.color.WB_Coeffs[LIBRAW_WBI_Daylight][1] = imgdata.color.WB_Coeffs[LIBRAW_WBI_Daylight][3] =
            imgdata.color.WB_Coeffs[LIBRAW_WBI_Tungsten][1] = imgdata.color.WB_Coeffs[LIBRAW_WBI_Tungsten][3] =
                imgdata.color.WB_Coeffs[LIBRAW_WBI_Flash][1] = imgdata.color.WB_Coeffs[LIBRAW_WBI_Flash][3] =
                    imgdata.color.WB_Coeffs[LIBRAW_WBI_Cloudy][1] = imgdata.color.WB_Coeffs[LIBRAW_WBI_Cloudy][3] =
                        imgdata.color.WB_Coeffs[LIBRAW_WBI_Shade][1] = imgdata.color.WB_Coeffs[LIBRAW_WBI_Shade][3] =
                            imgdata.color.WB_Coeffs[LIBRAW_WBI_FL_D][1] = imgdata.color.WB_Coeffs[LIBRAW_WBI_FL_D][3] =
                                imgdata.color.WB_Coeffs[LIBRAW_WBI_FL_N][1] =
                                    imgdata.color.WB_Coeffs[LIBRAW_WBI_FL_N][3] =
                                        imgdata.color.WB_Coeffs[LIBRAW_WBI_FL_W][1] =
                                            imgdata.color.WB_Coeffs[LIBRAW_WBI_FL_W][3] =
                                                imgdata.color.WB_Coeffs[LIBRAW_WBI_FL_WW][1] =
                                                    imgdata.color.WB_Coeffs[LIBRAW_WBI_FL_WW][3] = 0x100;
      }
      break;
#endif
    case 0x574247: /* WBG */
      get4();
      i = strcmp(model, "DiMAGE A200") ? 0 : 3;
      FORC4 cam_mul[c ^ (c >> 1) ^ i] = get2();
      break;
    case 0x545457: /* TTW */
      parse_tiff(ftell(ifp));
      data_offset = offset;
    }
    fseek(ifp, save + len + 8, SEEK_SET);
  }
  raw_height = high;
  raw_width = wide;
  order = sorder;
}


static int getwords(char *line, char *words[], int maxwords, int maxlen)
{
  line[maxlen - 1] = 0;
  char *p = line;
  int nwords = 0;

  while (1)
  {
    while (isspace(*p))
      p++;
    if (*p == '\0')
      return nwords;
    words[nwords++] = p;
    while (!isspace(*p) && *p != '\0')
      p++;
    if (*p == '\0')
      return nwords;
    *p++ = '\0';
    if (nwords >= maxwords)
      return nwords;
  }
}


void CLASS xtrans_interpolate(int passes)
{
  int c, d, f, g, h, i, v, ng, row, col, top, left, mrow, mcol;
  int val, ndir, pass, hm[8], avg[4], color[3][8];
  static const short orth[12] = {1, 0, 0, 1, -1, 0, 0, -1, 1, 0, 0, 1},
                     patt[2][16] = {{0, 1, 0, -1, 2, 0, -1, 0, 1, 1, 1, -1, 0, 0, 0, 0},
                                    {0, 1, 0, -2, 1, 0, -2, 0, 1, 1, -2, -2, 1, -1, -1, 1}},
                     dir[4] = {1, TS, TS + 1, TS - 1};
  short allhex[3][3][2][8], *hex;
  ushort min, max, sgrow, sgcol;
  ushort(*rgb)[TS][TS][3], (*rix)[3], (*pix)[4];
  short(*lab)[TS][3], (*lix)[3];
  float(*drv)[TS][TS], diff[6], tr;
  char(*homo)[TS][TS], *buffer;

#ifdef DCRAW_VERBOSE
  if (verbose)
    fprintf(stderr, _("%d-pass X-Trans interpolation...\n"), passes);
#endif

  cielab(0, 0);
  ndir = 4 << (passes > 1);
  buffer = (char *)malloc(TS * TS * (ndir * 11 + 6));
  merror(buffer, "xtrans_interpolate()");
  rgb = (ushort(*)[TS][TS][3])buffer;
  lab = (short(*)[TS][3])(buffer + TS * TS * (ndir * 6));
  drv = (float(*)[TS][TS])(buffer + TS * TS * (ndir * 6 + 6));
  homo = (char(*)[TS][TS])(buffer + TS * TS * (ndir * 10 + 6));

  /* Map a green hexagon around each non-green pixel and vice versa:	*/
  for (row = 0; row < 3; row++)
    for (col = 0; col < 3; col++)
      for (ng = d = 0; d < 10; d += 2)
      {
        g = fcol(row, col) == 1;
        if (fcol(row + orth[d], col + orth[d + 2]) == 1)
          ng = 0;
        else
          ng++;
        if (ng == 4)
        {
          sgrow = row;
          sgcol = col;
        }
        if (ng == g + 1)
          FORC(8)
          {
            v = orth[d] * patt[g][c * 2] + orth[d + 1] * patt[g][c * 2 + 1];
            h = orth[d + 2] * patt[g][c * 2] + orth[d + 3] * patt[g][c * 2 + 1];
            allhex[row][col][0][c ^ (g * 2 & d)] = h + v * width;
            allhex[row][col][1][c ^ (g * 2 & d)] = h + v * TS;
          }
      }

  /* Set green1 and green3 to the minimum and maximum allowed values:	*/
  for (row = 2; row < height - 2; row++)
    for (min = ~(max = 0), col = 2; col < width - 2; col++)
    {
      if (fcol(row, col) == 1 && (min = ~(max = 0)))
        continue;
      pix = image + row * width + col;
      hex = allhex[row % 3][col % 3][0];
      if (!max)
        FORC(6)
        {
          val = pix[hex[c]][1];
          if (min > val)
            min = val;
          if (max < val)
            max = val;
        }
      pix[0][1] = min;
      pix[0][3] = max;
      switch ((row - sgrow) % 3)
      {
      case 1:
        if (row < height - 3)
        {
          row++;
          col--;
        }
        break;
      case 2:
        if ((min = ~(max = 0)) && (col += 2) < width - 3 && row > 2)
          row--;
      }
    }

  for (top = 3; top < height - 19; top += TS - 16)
    for (left = 3; left < width - 19; left += TS - 16)
    {
      mrow = MIN(top + TS, height - 3);
      mcol = MIN(left + TS, width - 3);
      for (row = top; row < mrow; row++)
        for (col = left; col < mcol; col++)
          memcpy(rgb[0][row - top][col - left], image[row * width + col], 6);
      FORC3 memcpy(rgb[c + 1], rgb[0], sizeof *rgb);

      /* Interpolate green horizontally, vertically, and along both diagonals: */
      for (row = top; row < mrow; row++)
        for (col = left; col < mcol; col++)
        {
          if ((f = fcol(row, col)) == 1)
            continue;
          pix = image + row * width + col;
          hex = allhex[row % 3][col % 3][0];
          color[1][0] = 174 * (pix[hex[1]][1] + pix[hex[0]][1]) - 46 * (pix[2 * hex[1]][1] + pix[2 * hex[0]][1]);
          color[1][1] = 223 * pix[hex[3]][1] + pix[hex[2]][1] * 33 + 92 * (pix[0][f] - pix[-hex[2]][f]);
          FORC(2)
          color[1][2 + c] = 164 * pix[hex[4 + c]][1] + 92 * pix[-2 * hex[4 + c]][1] +
                            33 * (2 * pix[0][f] - pix[3 * hex[4 + c]][f] - pix[-3 * hex[4 + c]][f]);
          FORC4 rgb[c ^ !((row - sgrow) % 3)][row - top][col - left][1] = LIM(color[1][c] >> 8, pix[0][1], pix[0][3]);
        }

      for (pass = 0; pass < passes; pass++)
      {
        if (pass == 1)
          memcpy(rgb += 4, buffer, 4 * sizeof *rgb);

        /* Recalculate green from interpolated values of closer pixels:	*/
        if (pass)
        {
          for (row = top + 2; row < mrow - 2; row++)
            for (col = left + 2; col < mcol - 2; col++)
            {
              if ((f = fcol(row, col)) == 1)
                continue;
              pix = image + row * width + col;
              hex = allhex[row % 3][col % 3][1];
              for (d = 3; d < 6; d++)
              {
                rix = &rgb[(d - 2) ^ !((row - sgrow) % 3)][row - top][col - left];
                val =
                    rix[-2 * hex[d]][1] + 2 * rix[hex[d]][1] - rix[-2 * hex[d]][f] - 2 * rix[hex[d]][f] + 3 * rix[0][f];
                rix[0][1] = LIM(val / 3, pix[0][1], pix[0][3]);
              }
            }
        }

        /* Interpolate red and blue values for solitary green pixels:	*/
        for (row = (top - sgrow + 4) / 3 * 3 + sgrow; row < mrow - 2; row += 3)
          for (col = (left - sgcol + 4) / 3 * 3 + sgcol; col < mcol - 2; col += 3)
          {
            rix = &rgb[0][row - top][col - left];
            h = fcol(row, col + 1);
            memset(diff, 0, sizeof diff);
            for (i = 1, d = 0; d < 6; d++, i ^= TS ^ 1, h ^= 2)
            {
              for (c = 0; c < 2; c++, h ^= 2)
              {
                g = 2 * rix[0][1] - rix[i << c][1] - rix[-i << c][1];
                color[h][d] = g + rix[i << c][h] + rix[-i << c][h];
                if (d > 1)
                  diff[d] += SQR(rix[i << c][1] - rix[-i << c][1] - rix[i << c][h] + rix[-i << c][h]) + SQR(g);
              }
              if (d > 1 && (d & 1))
                if (diff[d - 1] < diff[d])
                  FORC(2) color[c * 2][d] = color[c * 2][d - 1];
              if (d < 2 || (d & 1))
              {
                FORC(2) rix[0][c * 2] = CLIP(color[c * 2][d] / 2);
                rix += TS * TS;
              }
            }
          }

        /* Interpolate red for blue pixels and vice versa:		*/
        for (row = top + 3; row < mrow - 3; row++)
          for (col = left + 3; col < mcol - 3; col++)
          {
            if ((f = 2 - fcol(row, col)) == 1)
              continue;
            rix = &rgb[0][row - top][col - left];
            c = (row - sgrow) % 3 ? TS : 1;
            h = 3 * (c ^ TS ^ 1);
            for (d = 0; d < 4; d++, rix += TS * TS)
            {
              i = d > 1 || ((d ^ c) & 1) || ((ABS(rix[0][1] - rix[c][1]) + ABS(rix[0][1] - rix[-c][1])) <
                                             2 * (ABS(rix[0][1] - rix[h][1]) + ABS(rix[0][1] - rix[-h][1])))
                      ? c
                      : h;
              rix[0][f] = CLIP((rix[i][f] + rix[-i][f] + 2 * rix[0][1] - rix[i][1] - rix[-i][1]) / 2);
            }
          }

        /* Fill in red and blue for 2x2 blocks of green:		*/
        for (row = top + 2; row < mrow - 2; row++)
          if ((row - sgrow) % 3)
            for (col = left + 2; col < mcol - 2; col++)
              if ((col - sgcol) % 3)
              {
                rix = &rgb[0][row - top][col - left];
                hex = allhex[row % 3][col % 3][1];
                for (d = 0; d < ndir; d += 2, rix += TS * TS)
                  if (hex[d] + hex[d + 1])
                  {
                    g = 3 * rix[0][1] - 2 * rix[hex[d]][1] - rix[hex[d + 1]][1];
                    for (c = 0; c < 4; c += 2)
                      rix[0][c] = CLIP((g + 2 * rix[hex[d]][c] + rix[hex[d + 1]][c]) / 3);
                  }
                  else
                  {
                    g = 2 * rix[0][1] - rix[hex[d]][1] - rix[hex[d + 1]][1];
                    for (c = 0; c < 4; c += 2)
                      rix[0][c] = CLIP((g + rix[hex[d]][c] + rix[hex[d + 1]][c]) / 2);
                  }
              }
      }
      rgb = (ushort(*)[TS][TS][3])buffer;
      mrow -= top;
      mcol -= left;

      /* Convert to CIELab and differentiate in all directions:	*/
      for (d = 0; d < ndir; d++)
      {
        for (row = 2; row < mrow - 2; row++)
          for (col = 2; col < mcol - 2; col++)
            cielab(rgb[d][row][col], lab[row][col]);
        for (f = dir[d & 3], row = 3; row < mrow - 3; row++)
          for (col = 3; col < mcol - 3; col++)
          {
            lix = &lab[row][col];
            g = 2 * lix[0][0] - lix[f][0] - lix[-f][0];
            drv[d][row][col] = SQR(g) + SQR((2 * lix[0][1] - lix[f][1] - lix[-f][1] + g * 500 / 232)) +
                               SQR((2 * lix[0][2] - lix[f][2] - lix[-f][2] - g * 500 / 580));
          }
      }

      /* Build homogeneity maps from the derivatives:			*/
      memset(homo, 0, ndir * TS * TS);
      for (row = 4; row < mrow - 4; row++)
        for (col = 4; col < mcol - 4; col++)
        {
          for (tr = FLT_MAX, d = 0; d < ndir; d++)
            if (tr > drv[d][row][col])
              tr = drv[d][row][col];
          tr *= 8;
          for (d = 0; d < ndir; d++)
            for (v = -1; v <= 1; v++)
              for (h = -1; h <= 1; h++)
                if (drv[d][row + v][col + h] <= tr)
                  homo[d][row][col]++;
        }

      /* Average the most homogenous pixels for the final result:	*/
      if (height - top < TS + 4)
        mrow = height - top + 2;
      if (width - left < TS + 4)
        mcol = width - left + 2;
      for (row = MIN(top, 8); row < mrow - 8; row++)
        for (col = MIN(left, 8); col < mcol - 8; col++)
        {
          for (d = 0; d < ndir; d++)
            for (hm[d] = 0, v = -2; v <= 2; v++)
              for (h = -2; h <= 2; h++)
                hm[d] += homo[d][row + v][col + h];
          for (d = 0; d < ndir - 4; d++)
            if (hm[d] < hm[d + 4])
              hm[d] = 0;
            else if (hm[d] > hm[d + 4])
              hm[d + 4] = 0;
          for (max = hm[0], d = 1; d < ndir; d++)
            if (max < hm[d])
              max = hm[d];
          max -= max >> 3;
          memset(avg, 0, sizeof avg);
          for (d = 0; d < ndir; d++)
            if (hm[d] >= max)
            {
              FORC3 avg[c] += rgb[d][row][col][c];
              avg[3]++;
            }
          FORC3 image[(row + top) * width + col + left][c] = avg[c] / avg[3];
        }
    }
  free(buffer);
  border_interpolate(8);
}


void CLASS ahd_interpolate_r_and_b_and_convert_to_cielab(int top, int left, ushort (*inout_rgb)[TS][TS][3],
                                                         short (*out_lab)[TS][TS][3])
{
  int direction;
  for (direction = 0; direction < 2; direction++)
  {
    ahd_interpolate_r_and_b_in_rgb_and_convert_to_cielab(top, left, inout_rgb[direction], out_lab[direction]);
  }
}


void CLASS packed_load_raw()
{
  int vbits = 0, bwide, rbits, bite, half, irow, row, col, val, i;
  UINT64 bitbuf = 0;

  bwide = raw_width * tiff_bps / 8;
  bwide += bwide & load_flags >> 7;
  rbits = bwide * 8 - raw_width * tiff_bps;
  if (load_flags & 1)
    bwide = bwide * 16 / 15;
  bite = 8 + (load_flags & 24);
  half = (raw_height + 1) >> 1;
  for (irow = 0; irow < raw_height; irow++)
  {
#ifdef LIBRAW_LIBRARY_BUILD
    checkCancel();
#endif
    row = irow;
    if (load_flags & 2 && (row = irow % half * 2 + irow / half) == 1 && load_flags & 4)
    {
      if (vbits = 0, tiff_compress)
        fseek(ifp, data_offset - (-half * bwide & -2048), SEEK_SET);
      else
      {
        fseek(ifp, 0, SEEK_END);
        fseek(ifp, ftell(ifp) >> 3 << 2, SEEK_SET);
      }
    }
    for (col = 0; col < raw_width; col++)
    {
      for (vbits -= tiff_bps; vbits < 0; vbits += bite)
      {
        bitbuf <<= bite;
        for (i = 0; i < bite; i += 8)
          bitbuf |= (unsigned)(fgetc(ifp) << i);
      }
      val = bitbuf << (64 - tiff_bps - vbits) >> (64 - tiff_bps);
      RAW(row, col ^ (load_flags >> 6 & 1)) = val;
      if (load_flags & 1 && (col % 10) == 9 && fgetc(ifp) && row < height + top_margin && col < width + left_margin)
        derror();
    }
    vbits -= rbits;
  }
}


void CLASS setCanonBodyFeatures(unsigned id)
{
  imgdata.lens.makernotes.CamID = id;
  if ((id == 0x80000001) || // 1D
      (id == 0x80000174) || // 1D2
      (id == 0x80000232) || // 1D2N
      (id == 0x80000169) || // 1D3
      (id == 0x80000281)    // 1D4
      )
  {
    imgdata.lens.makernotes.CameraFormat = LIBRAW_FORMAT_APSH;
    imgdata.lens.makernotes.CameraMount = LIBRAW_MOUNT_Canon_EF;
  }
  else if ((id == 0x80000167) || // 1Ds
           (id == 0x80000188) || // 1Ds2
           (id == 0x80000215) || // 1Ds3
           (id == 0x80000269) || // 1DX
           (id == 0x80000328) || // 1DX2
           (id == 0x80000324) || // 1DC
           (id == 0x80000213) || // 5D
           (id == 0x80000218) || // 5D2
           (id == 0x80000285) || // 5D3
           (id == 0x80000349) || // 5D4
           (id == 0x80000382) || // 5DS
           (id == 0x80000401) || // 5DS R
           (id == 0x80000302)    // 6D
           )
  {
    imgdata.lens.makernotes.CameraFormat = LIBRAW_FORMAT_FF;
    imgdata.lens.makernotes.CameraMount = LIBRAW_MOUNT_Canon_EF;
  }
  else if ((id == 0x80000331) || // M
           (id == 0x80000355) || // M2
           (id == 0x80000374) || // M3
           (id == 0x80000384) || // M10
           (id == 0x80000394)    // M5
           )
  {
    imgdata.lens.makernotes.CameraFormat = LIBRAW_FORMAT_APSC;
    imgdata.lens.makernotes.CameraMount = LIBRAW_MOUNT_Canon_EF_M;
  }
  else if ((id == 0x01140000) || // D30
           (id == 0x01668000) || // D60
           (id > 0x80000000))
  {
    imgdata.lens.makernotes.CameraFormat = LIBRAW_FORMAT_APSC;
    imgdata.lens.makernotes.CameraMount = LIBRAW_MOUNT_Canon_EF;
    imgdata.lens.makernotes.LensMount = LIBRAW_MOUNT_Unknown;
  }
  else
  {
    imgdata.lens.makernotes.CameraMount = LIBRAW_MOUNT_FixedLens;
    imgdata.lens.makernotes.LensMount = LIBRAW_MOUNT_FixedLens;
  }

  return;
}


void CLASS ppg_interpolate()
{
  int dir[5] = {1, width, -1, -width, 1};
  int row, col, diff[2], guess[2], c, d, i;
  ushort(*pix)[4];

  border_interpolate(3);
#ifdef DCRAW_VERBOSE
  if (verbose)
    fprintf(stderr, _("PPG interpolation...\n"));
#endif

/*  Fill in the green layer with gradients and pattern recognition: */
#ifdef LIBRAW_LIBRARY_BUILD
  RUN_CALLBACK(LIBRAW_PROGRESS_INTERPOLATE, 0, 3);
#ifdef LIBRAW_USE_OPENMP
#pragma omp parallel for default(shared) private(guess, diff, row, col, d, c, i, pix) schedule(static)
#endif
#endif
  for (row = 3; row < height - 3; row++)
    for (col = 3 + (FC(row, 3) & 1), c = FC(row, col); col < width - 3; col += 2)
    {
      pix = image + row * width + col;
      for (i = 0; (d = dir[i]) > 0; i++)
      {
        guess[i] = (pix[-d][1] + pix[0][c] + pix[d][1]) * 2 - pix[-2 * d][c] - pix[2 * d][c];
        diff[i] = (ABS(pix[-2 * d][c] - pix[0][c]) + ABS(pix[2 * d][c] - pix[0][c]) + ABS(pix[-d][1] - pix[d][1])) * 3 +
                  (ABS(pix[3 * d][1] - pix[d][1]) + ABS(pix[-3 * d][1] - pix[-d][1])) * 2;
      }
      d = dir[i = diff[0] > diff[1]];
      pix[0][1] = ULIM(guess[i] >> 2, pix[d][1], pix[-d][1]);
    }
/*  Calculate red and blue for each green pixel:		*/
#ifdef LIBRAW_LIBRARY_BUILD
  RUN_CALLBACK(LIBRAW_PROGRESS_INTERPOLATE, 1, 3);
#ifdef LIBRAW_USE_OPENMP
#pragma omp parallel for default(shared) private(guess, diff, row, col, d, c, i, pix) schedule(static)
#endif
#endif
  for (row = 1; row < height - 1; row++)
    for (col = 1 + (FC(row, 2) & 1), c = FC(row, col + 1); col < width - 1; col += 2)
    {
      pix = image + row * width + col;
      for (i = 0; (d = dir[i]) > 0; c = 2 - c, i++)
        pix[0][c] = CLIP((pix[-d][c] + pix[d][c] + 2 * pix[0][1] - pix[-d][1] - pix[d][1]) >> 1);
    }
/*  Calculate blue for red pixels and vice versa:		*/
#ifdef LIBRAW_LIBRARY_BUILD
  RUN_CALLBACK(LIBRAW_PROGRESS_INTERPOLATE, 2, 3);
#ifdef LIBRAW_USE_OPENMP
#pragma omp parallel for default(shared) private(guess, diff, row, col, d, c, i, pix) schedule(static)
#endif
#endif
  for (row = 1; row < height - 1; row++)
    for (col = 1 + (FC(row, 1) & 1), c = 2 - FC(row, col); col < width - 1; col += 2)
    {
      pix = image + row * width + col;
      for (i = 0; (d = dir[i] + dir[i + 1]) > 0; i++)
      {
        diff[i] = ABS(pix[-d][c] - pix[d][c]) + ABS(pix[-d][1] - pix[0][1]) + ABS(pix[d][1] - pix[0][1]);
        guess[i] = pix[-d][c] + pix[d][c] + 2 * pix[0][1] - pix[-d][1] - pix[d][1];
      }
      if (diff[0] != diff[1])
        pix[0][c] = CLIP(guess[diff[0] > diff[1]] >> 1);
      else
        pix[0][c] = CLIP((guess[0] + guess[1]) >> 2);
    }
}


void CLASS nokia_load_raw()
{
  uchar *data, *dp;
  int rev, dwide, row, col, c;
  double sum[] = {0, 0};

  rev = 3 * (order == 0x4949);
  dwide = (raw_width * 5 + 1) / 4;
  data = (uchar *)malloc(dwide * 2);
  merror(data, "nokia_load_raw()");
#ifdef LIBRAW_LIBRARY_BUILD
  try
  {
#endif
    for (row = 0; row < raw_height; row++)
    {
#ifdef LIBRAW_LIBRARY_BUILD
      checkCancel();
#endif
      if (fread(data + dwide, 1, dwide, ifp) < dwide)
        derror();
      FORC(dwide) data[c] = data[dwide + (c ^ rev)];
      for (dp = data, col = 0; col < raw_width; dp += 5, col += 4)
        FORC4 RAW(row, col + c) = (dp[c] << 2) | (dp[4] >> (c << 1) & 3);
    }
#ifdef LIBRAW_LIBRARY_BUILD
  }
  catch (...)
  {
    free(data);
    throw;
  }
#endif
  free(data);
  maximum = 0x3ff;
  if (strncmp(make, "OmniVision", 10))
    return;
  row = raw_height / 2;
  FORC(width - 1)
  {
    sum[c & 1] += SQR(RAW(row, c) - RAW(row + 1, c + 1));
    sum[~c & 1] += SQR(RAW(row + 1, c) - RAW(row, c + 1));
  }
  if (sum[1] > sum[0])
    filters = 0x4b4b4b4b;
}


void CLASS lossless_dng_load_raw()
{
  unsigned save, trow = 0, tcol = 0, jwide, jrow, jcol, row, col, i, j;
  struct jhead jh;
  ushort *rp;

  while (trow < raw_height)
  {
#ifdef LIBRAW_LIBRARY_BUILD
    checkCancel();
#endif
    save = ftell(ifp);
    if (tile_length < INT_MAX)
      fseek(ifp, get4(), SEEK_SET);
    if (!ljpeg_start(&jh, 0))
      break;
    jwide = jh.wide;
    if (filters)
      jwide *= jh.clrs;
    jwide /= MIN(is_raw, tiff_samples);
#ifdef LIBRAW_LIBRARY_BUILD
    try
    {
#endif
      switch (jh.algo)
      {
      case 0xc1:
        jh.vpred[0] = 16384;
        getbits(-1);
        for (jrow = 0; jrow + 7 < jh.high; jrow += 8)
        {
#ifdef LIBRAW_LIBRARY_BUILD
          checkCancel();
#endif
          for (jcol = 0; jcol + 7 < jh.wide; jcol += 8)
          {
            ljpeg_idct(&jh);
            rp = jh.idct;
            row = trow + jcol / tile_width + jrow * 2;
            col = tcol + jcol % tile_width;
            for (i = 0; i < 16; i += 2)
              for (j = 0; j < 8; j++)
                adobe_copy_pixel(row + i, col + j, &rp);
          }
        }
        break;
      case 0xc3:
        for (row = col = jrow = 0; jrow < jh.high; jrow++)
        {
#ifdef LIBRAW_LIBRARY_BUILD
          checkCancel();
#endif
          rp = ljpeg_row(jrow, &jh);
          for (jcol = 0; jcol < jwide; jcol++)
          {
            adobe_copy_pixel(trow + row, tcol + col, &rp);
            if (++col >= tile_width || col >= raw_width)
              row += 1 + (col = 0);
          }
        }
      }
#ifdef LIBRAW_LIBRARY_BUILD
    }
    catch (...)
    {
      ljpeg_end(&jh);
      throw;
    }
#endif
    fseek(ifp, save + 4, SEEK_SET);
    if ((tcol += tile_width) >= raw_width)
      trow += tile_length + (tcol = 0);
    ljpeg_end(&jh);
  }
}


short *CLASS foveon_make_curve(double max, double mul, double filt)
{
  short *curve;
  unsigned i, size;
  double x;

  if (!filt)
    filt = 0.8;
  size = 4 * M_PI * max / filt;
  if (size == UINT_MAX)
    size--;
  curve = (short *)calloc(size + 1, sizeof *curve);
  merror(curve, "foveon_make_curve()");
  curve[0] = size;
  for (i = 0; i < size; i++)
  {
    x = i * filt / max / 4;
    curve[i + 1] = (cos(x) + 1) / 2 * tanh(i * filt / mul) * mul + 0.5;
  }
  return curve;
}


void CLASS parse_external_jpeg()
{
  const char *file, *ext;
  char *jname, *jfile, *jext;
#ifndef LIBRAW_LIBRARY_BUILD
  FILE *save = ifp;
#else
#if defined(_WIN32) && !defined(__MINGW32__) && defined(_MSC_VER) && (_MSC_VER > 1310)
  if (ifp->wfname())
  {
    std::wstring rawfile(ifp->wfname());
    rawfile.replace(rawfile.length() - 3, 3, L"JPG");
    if (!ifp->subfile_open(rawfile.c_str()))
    {
      parse_tiff(12);
      thumb_offset = 0;
      is_raw = 1;
      ifp->subfile_close();
    }
    else
      imgdata.process_warnings |= LIBRAW_WARN_NO_METADATA;
    return;
  }
#endif
  if (!ifp->fname())
  {
    imgdata.process_warnings |= LIBRAW_WARN_NO_METADATA;
    return;
  }
#endif

  ext = strrchr(ifname, '.');
  file = strrchr(ifname, '/');
  if (!file)
    file = strrchr(ifname, '\\');
#ifndef LIBRAW_LIBRARY_BUILD
  if (!file)
    file = ifname - 1;
#else
  if (!file)
    file = (char *)ifname - 1;
#endif
  file++;
  if (!ext || strlen(ext) != 4 || ext - file != 8)
    return;
  jname = (char *)malloc(strlen(ifname) + 1);
  merror(jname, "parse_external_jpeg()");
  strcpy(jname, ifname);
  jfile = file - ifname + jname;
  jext = ext - ifname + jname;
  if (strcasecmp(ext, ".jpg"))
  {
    strcpy(jext, isupper(ext[1]) ? ".JPG" : ".jpg");
    if (isdigit(*file))
    {
      memcpy(jfile, file + 4, 4);
      memcpy(jfile + 4, file, 4);
    }
  }
  else
    while (isdigit(*--jext))
    {
      if (*jext != '9')
      {
        (*jext)++;
        break;
      }
      *jext = '0';
    }
#ifndef LIBRAW_LIBRARY_BUILD
  if (strcmp(jname, ifname))
  {
    if ((ifp = fopen(jname, "rb")))
    {
#ifdef DCRAW_VERBOSE
      if (verbose)
        fprintf(stderr, _("Reading metadata from %s ...\n"), jname);
#endif
      parse_tiff(12);
      thumb_offset = 0;
      is_raw = 1;
      fclose(ifp);
    }
  }
#else
  if (strcmp(jname, ifname))
  {
    if (!ifp->subfile_open(jname))
    {
      parse_tiff(12);
      thumb_offset = 0;
      is_raw = 1;
      ifp->subfile_close();
    }
    else
      imgdata.process_warnings |= LIBRAW_WARN_NO_METADATA;
  }
#endif
  if (!timestamp)
  {
#ifdef LIBRAW_LIBRARY_BUILD
    imgdata.process_warnings |= LIBRAW_WARN_NO_METADATA;
#endif
#ifdef DCRAW_VERBOSE
    fprintf(stderr, _("Failed to read metadata from %s\n"), jname);
#endif
  }
  free(jname);
#ifndef LIBRAW_LIBRARY_BUILD
  ifp = save;
#endif
}


void CLASS foveon_thumb()
{
  unsigned bwide, row, col, bitbuf = 0, bit = 1, c, i;
  char *buf;
  struct decode *dindex;
  short pred[3];

  bwide = get4();
  fprintf(ofp, "P6\n%d %d\n255\n", thumb_width, thumb_height);
  if (bwide > 0)
  {
    if (bwide < thumb_width * 3)
      return;
    buf = (char *)malloc(bwide);
    merror(buf, "foveon_thumb()");
    for (row = 0; row < thumb_height; row++)
    {
      fread(buf, 1, bwide, ifp);
      fwrite(buf, 3, thumb_width, ofp);
    }
    free(buf);
    return;
  }
  foveon_decoder(256, 0);

  for (row = 0; row < thumb_height; row++)
  {
    memset(pred, 0, sizeof pred);
    if (!bit)
      get4();
    for (bit = col = 0; col < thumb_width; col++)
      FORC3
      {
        for (dindex = first_decode; dindex->branch[0];)
        {
          if ((bit = (bit - 1) & 31) == 31)
            for (i = 0; i < 4; i++)
              bitbuf = (bitbuf << 8) + fgetc(ifp);
          dindex = dindex->branch[bitbuf >> bit & 1];
        }
        pred[c] += dindex->leaf;
        fputc(pred[c], ofp);
      }
  }
}


void CLASS wavelet_denoise()
{
  float *fimg = 0, *temp, thold, mul[2], avg, diff;
  int scale = 1, size, lev, hpass, lpass, row, col, nc, c, i, wlast, blk[2];
  ushort *window[4];
  static const float noise[] = {0.8002, 0.2735, 0.1202, 0.0585, 0.0291, 0.0152, 0.0080, 0.0044};

#ifdef DCRAW_VERBOSE
  if (verbose)
    fprintf(stderr, _("Wavelet denoising...\n"));
#endif

  while (maximum << scale < 0x10000)
    scale++;
  maximum <<= --scale;
  black <<= scale;
  FORC4 cblack[c] <<= scale;
  if ((size = iheight * iwidth) < 0x15550000)
    fimg = (float *)malloc((size * 3 + iheight + iwidth) * sizeof *fimg);
  merror(fimg, "wavelet_denoise()");
  temp = fimg + size * 3;
  if ((nc = colors) == 3 && filters)
    nc++;
#ifdef LIBRAW_LIBRARY_BUILD
#pragma omp parallel default(shared) private(i, col, row, thold, lev, lpass, hpass, temp, c) firstprivate(scale, size)
#endif
  {
    temp = (float *)malloc((iheight + iwidth) * sizeof *fimg);
    FORC(nc)
    { /* denoise R,G1,B,G3 individually */
#ifdef LIBRAW_LIBRARY_BUILD
#pragma omp for
#endif
      for (i = 0; i < size; i++)
        fimg[i] = 256 * sqrt((double)(image[i][c] << scale));
      for (hpass = lev = 0; lev < 5; lev++)
      {
        lpass = size * ((lev & 1) + 1);
#ifdef LIBRAW_LIBRARY_BUILD
#pragma omp for
#endif
        for (row = 0; row < iheight; row++)
        {
          hat_transform(temp, fimg + hpass + row * iwidth, 1, iwidth, 1 << lev);
          for (col = 0; col < iwidth; col++)
            fimg[lpass + row * iwidth + col] = temp[col] * 0.25;
        }
#ifdef LIBRAW_LIBRARY_BUILD
#pragma omp for
#endif
        for (col = 0; col < iwidth; col++)
        {
          hat_transform(temp, fimg + lpass + col, iwidth, iheight, 1 << lev);
          for (row = 0; row < iheight; row++)
            fimg[lpass + row * iwidth + col] = temp[row] * 0.25;
        }
        thold = threshold * noise[lev];
#ifdef LIBRAW_LIBRARY_BUILD
#pragma omp for
#endif
        for (i = 0; i < size; i++)
        {
          fimg[hpass + i] -= fimg[lpass + i];
          if (fimg[hpass + i] < -thold)
            fimg[hpass + i] += thold;
          else if (fimg[hpass + i] > thold)
            fimg[hpass + i] -= thold;
          else
            fimg[hpass + i] = 0;
          if (hpass)
            fimg[i] += fimg[hpass + i];
        }
        hpass = lpass;
      }
#ifdef LIBRAW_LIBRARY_BUILD
#pragma omp for
#endif
      for (i = 0; i < size; i++)
        image[i][c] = CLIP(SQR(fimg[i] + fimg[lpass + i]) / 0x10000);
    }
    free(temp);
  } /* end omp parallel */
  /* the following loops are hard to parallize, no idea yes,
   * problem is wlast which is carrying dependency
   * second part should be easyer, but did not yet get it right.
   */
  if (filters && colors == 3)
  { /* pull G1 and G3 closer together */
    for (row = 0; row < 2; row++)
    {
      mul[row] = 0.125 * pre_mul[FC(row + 1, 0) | 1] / pre_mul[FC(row, 0) | 1];
      blk[row] = cblack[FC(row, 0) | 1];
    }
    for (i = 0; i < 4; i++)
      window[i] = (ushort *)fimg + width * i;
    for (wlast = -1, row = 1; row < height - 1; row++)
    {
      while (wlast < row + 1)
      {
        for (wlast++, i = 0; i < 4; i++)
          window[(i + 3) & 3] = window[i];
        for (col = FC(wlast, 1) & 1; col < width; col += 2)
          window[2][col] = BAYER(wlast, col);
      }
      thold = threshold / 512;
      for (col = (FC(row, 0) & 1) + 1; col < width - 1; col += 2)
      {
        avg = (window[0][col - 1] + window[0][col + 1] + window[2][col - 1] + window[2][col + 1] - blk[~row & 1] * 4) *
                  mul[row & 1] +
              (window[1][col] + blk[row & 1]) * 0.5;
        avg = avg < 0 ? 0 : sqrt(avg);
        diff = sqrt((double)BAYER(row, col)) - avg;
        if (diff < -thold)
          diff += thold;
        else if (diff > thold)
          diff -= thold;
        else
          diff = 0;
        BAYER(row, col) = CLIP(SQR(avg + diff) + 0.5);
      }
    }
  }
  free(fimg);
}


void CLASS ahd_interpolate_combine_homogeneous_pixels(int top, int left, ushort (*rgb)[TS][TS][3],
                                                      char (*homogeneity_map)[TS][2])
{
  int row, col;
  int tr, tc;
  int i, j;
  int direction;
  int hm[2];
  int c;
  const int rowlimit = MIN(top + TS - 3, height - 5);
  const int collimit = MIN(left + TS - 3, width - 5);

  ushort(*pix)[4];
  ushort(*rix[2])[3];

  for (row = top + 3; row < rowlimit; row++)
  {
    tr = row - top;
    pix = &image[row * width + left + 2];
    for (direction = 0; direction < 2; direction++)
    {
      rix[direction] = &rgb[direction][tr][2];
    }

    for (col = left + 3; col < collimit; col++)
    {
      tc = col - left;
      pix++;
      for (direction = 0; direction < 2; direction++)
      {
        rix[direction]++;
      }

      for (direction = 0; direction < 2; direction++)
      {
        hm[direction] = 0;
        for (i = tr - 1; i <= tr + 1; i++)
        {
          for (j = tc - 1; j <= tc + 1; j++)
          {
            hm[direction] += homogeneity_map[i][j][direction];
          }
        }
      }
      if (hm[0] != hm[1])
      {
        memcpy(pix[0], rix[hm[1] > hm[0]][0], 3 * sizeof(ushort));
      }
      else
      {
        FORC3 { pix[0][c] = (rix[0][0][c] + rix[1][0][c]) >> 1; }
      }
    }
  }
}


static int lp_register(int nr, struct parport *port)
{
	lp_table[nr].dev = parport_register_device(port, "lp",
						   lp_preempt, NULL, NULL, 0,
						   (void *) &lp_table[nr]);
	if (lp_table[nr].dev == NULL)
		return 1;
	lp_table[nr].flags |= LP_EXIST;

	if (reset)
		lp_reset(nr);

	device_create(lp_class, port->dev, MKDEV(LP_MAJOR, nr), NULL,
		      "lp%d", nr);

	printk(KERN_INFO "lp%d: using %s (%s).\n", nr, port->name,
	       (port->irq == PARPORT_IRQ_NONE)?"polling":"interrupt-driven");

#ifdef CONFIG_LP_CONSOLE
	if (!nr) {
		if (port->modes & PARPORT_MODE_SAFEININT) {
			register_console(&lpcons);
			console_registered = port;
			printk (KERN_INFO "lp%d: console ready\n", CONSOLE_LP);
		} else
			printk (KERN_ERR "lp%d: cannot run console on %s\n",
				CONSOLE_LP, port->name);
	}
#endif

	return 0;
}


ZEND_VM_HOT_HANDLER(60, ZEND_DO_FCALL, ANY, ANY, SPEC(RETVAL))
{
	USE_OPLINE
	zend_execute_data *call = EX(call);
	zend_function *fbc = call->func;
	zval *ret;
	zval retval;

	SAVE_OPLINE();
	EX(call) = call->prev_execute_data;
	if (UNEXPECTED((fbc->common.fn_flags & (ZEND_ACC_ABSTRACT|ZEND_ACC_DEPRECATED)) != 0)) {
		if (UNEXPECTED((fbc->common.fn_flags & ZEND_ACC_ABSTRACT) != 0)) {
			zend_abstract_method(fbc);
ZEND_VM_C_LABEL(fcall_except):
			UNDEF_RESULT();
			if (!RETURN_VALUE_USED(opline)) {
				ret = &retval;
				ZVAL_UNDEF(ret);
			}
			ZEND_VM_C_GOTO(fcall_end);
		} else {
			zend_deprecated_function(fbc);
			if (UNEXPECTED(EG(exception) != NULL)) {
				ZEND_VM_C_GOTO(fcall_except);
			}
		}
	}

	if (EXPECTED(fbc->type == ZEND_USER_FUNCTION)) {
		ret = NULL;
		if (RETURN_VALUE_USED(opline)) {
			ret = EX_VAR(opline->result.var);
		}

		call->prev_execute_data = execute_data;
		execute_data = call;
		i_init_func_execute_data(&fbc->op_array, ret, 1 EXECUTE_DATA_CC);

		if (EXPECTED(zend_execute_ex == execute_ex)) {
			LOAD_OPLINE_EX();
			ZEND_VM_ENTER_EX();
		} else {
			SAVE_OPLINE_EX();
			execute_data = EX(prev_execute_data);
			LOAD_OPLINE();
			ZEND_ADD_CALL_FLAG(call, ZEND_CALL_TOP);
			zend_execute_ex(call);
		}
	} else if (EXPECTED(fbc->type < ZEND_USER_FUNCTION)) {
		call->prev_execute_data = execute_data;
		EG(current_execute_data) = call;

		if (UNEXPECTED(fbc->common.fn_flags & ZEND_ACC_HAS_TYPE_HINTS)
		  && UNEXPECTED(!zend_verify_internal_arg_types(fbc, call))) {
			ZEND_VM_C_GOTO(fcall_except);
		}

		ret = RETURN_VALUE_USED(opline) ? EX_VAR(opline->result.var) : &retval;
		ZVAL_NULL(ret);

		if (!zend_execute_internal) {
			/* saves one function call if zend_execute_internal is not used */
			fbc->internal_function.handler(call, ret);
		} else {
			zend_execute_internal(call, ret);
		}

#if ZEND_DEBUG
		if (!EG(exception) && call->func) {
			ZEND_ASSERT(!(call->func->common.fn_flags & ZEND_ACC_HAS_RETURN_TYPE) ||
				zend_verify_internal_return_type(call->func, ret));
			ZEND_ASSERT((call->func->common.fn_flags & ZEND_ACC_RETURN_REFERENCE)
				? Z_ISREF_P(ret) : !Z_ISREF_P(ret));
		}
#endif

		EG(current_execute_data) = execute_data;

ZEND_VM_C_LABEL(fcall_end):
		zend_vm_stack_free_args(call);
		if (!RETURN_VALUE_USED(opline)) {
			i_zval_ptr_dtor(ret);
		}
	} else { /* ZEND_OVERLOADED_FUNCTION */
		ret = RETURN_VALUE_USED(opline) ? EX_VAR(opline->result.var) : &retval;

		call->prev_execute_data = execute_data;

		if (UNEXPECTED(!zend_do_fcall_overloaded(call, ret))) {
			UNDEF_RESULT();
			HANDLE_EXCEPTION();
		}

		if (!RETURN_VALUE_USED(opline)) {
			zval_ptr_dtor(ret);
		}
	}

	if (UNEXPECTED(ZEND_CALL_INFO(call) & ZEND_CALL_RELEASE_THIS)) {
		OBJ_RELEASE(Z_OBJ(call->This));
	}

	zend_vm_stack_free_call_frame(call);
	if (UNEXPECTED(EG(exception) != NULL)) {
		zend_rethrow_exception(execute_data);
		HANDLE_EXCEPTION();
	}

	ZEND_VM_SET_OPCODE(opline + 1);
	ZEND_VM_CONTINUE();
}


ZEND_VM_HANDLER(139, ZEND_GENERATOR_CREATE, ANY, ANY)
{
	zval *return_value = EX(return_value);

	if (EXPECTED(return_value)) {
		USE_OPLINE
		zend_generator *generator;
		zend_execute_data *gen_execute_data;
		uint32_t num_args, used_stack, call_info;

		object_init_ex(return_value, zend_ce_generator);

		/*
		 * Normally the execute_data is allocated on the VM stack (because it does
		 * not actually do any allocation and thus is faster). For generators
		 * though this behavior would be suboptimal, because the (rather large)
		 * structure would have to be copied back and forth every time execution is
		 * suspended or resumed. That's why for generators the execution context
		 * is allocated on heap.
		 */
		num_args = EX_NUM_ARGS();
		if (EXPECTED(num_args <= EX(func)->op_array.num_args)) {
			used_stack = (ZEND_CALL_FRAME_SLOT + EX(func)->op_array.last_var + EX(func)->op_array.T) * sizeof(zval);
			gen_execute_data = (zend_execute_data*)emalloc(used_stack);
			used_stack = (ZEND_CALL_FRAME_SLOT + EX(func)->op_array.last_var) * sizeof(zval);
		} else {
			used_stack = (ZEND_CALL_FRAME_SLOT + num_args + EX(func)->op_array.last_var + EX(func)->op_array.T - EX(func)->op_array.num_args) * sizeof(zval);
			gen_execute_data = (zend_execute_data*)emalloc(used_stack);
		}
		memcpy(gen_execute_data, execute_data, used_stack);

		/* Save execution context in generator object. */
		generator = (zend_generator *) Z_OBJ_P(EX(return_value));
		generator->execute_data = gen_execute_data;
		generator->frozen_call_stack = NULL;
		generator->execute_fake.opline = NULL;
		generator->execute_fake.func = NULL;
		generator->execute_fake.prev_execute_data = NULL;
		ZVAL_OBJ(&generator->execute_fake.This, (zend_object *) generator);

		gen_execute_data->opline = opline + 1;
		/* EX(return_value) keeps pointer to zend_object (not a real zval) */
		gen_execute_data->return_value = (zval*)generator;
		call_info = Z_TYPE_INFO(EX(This));
		if ((call_info & Z_TYPE_MASK) == IS_OBJECT
		 && (!(call_info & (ZEND_CALL_CLOSURE|ZEND_CALL_RELEASE_THIS))
			 /* Bug #72523 */
			|| UNEXPECTED(zend_execute_ex != execute_ex))) {
			ZEND_ADD_CALL_FLAG_EX(call_info, ZEND_CALL_RELEASE_THIS);
			Z_ADDREF(gen_execute_data->This);
		}
		ZEND_ADD_CALL_FLAG_EX(call_info, (ZEND_CALL_TOP_FUNCTION | ZEND_CALL_ALLOCATED | ZEND_CALL_GENERATOR));
		Z_TYPE_INFO(gen_execute_data->This) = call_info;
		gen_execute_data->prev_execute_data = NULL;

		call_info = EX_CALL_INFO();
		EG(current_execute_data) = EX(prev_execute_data);
		if (EXPECTED(!(call_info & (ZEND_CALL_TOP|ZEND_CALL_ALLOCATED)))) {
			EG(vm_stack_top) = (zval*)execute_data;
			execute_data = EX(prev_execute_data);
			LOAD_NEXT_OPLINE();
			ZEND_VM_LEAVE();
		} else if (EXPECTED(!(call_info & ZEND_CALL_TOP))) {
			zend_execute_data *old_execute_data = execute_data;
			execute_data = EX(prev_execute_data);
			zend_vm_stack_free_call_frame_ex(call_info, old_execute_data);
			LOAD_NEXT_OPLINE();
			ZEND_VM_LEAVE();
		} else {
			ZEND_VM_RETURN();
		}
	} else {
		ZEND_VM_DISPATCH_TO_HELPER(zend_leave_helper);
	}
}


ZEND_VM_HOT_SEND_HANDLER(66, ZEND_SEND_VAR_EX, VAR|CV, NUM, SPEC(QUICK_ARG))
{
	USE_OPLINE
	zval *varptr, *arg;
	zend_free_op free_op1;
	uint32_t arg_num = opline->op2.num;

	if (EXPECTED(arg_num <= MAX_ARG_FLAG_NUM)) {
		if (QUICK_ARG_SHOULD_BE_SENT_BY_REF(EX(call)->func, arg_num)) {
			ZEND_VM_C_GOTO(send_var_by_ref);
		}
	} else if (ARG_SHOULD_BE_SENT_BY_REF(EX(call)->func, arg_num)) {
ZEND_VM_C_LABEL(send_var_by_ref):
		ZEND_VM_DISPATCH_TO_HANDLER(ZEND_SEND_REF);
	}

	varptr = GET_OP1_ZVAL_PTR_UNDEF(BP_VAR_R);
	if (OP1_TYPE == IS_CV && UNEXPECTED(Z_TYPE_INFO_P(varptr) == IS_UNDEF)) {
		SAVE_OPLINE();
		ZVAL_UNDEFINED_OP1();
		arg = ZEND_CALL_VAR(EX(call), opline->result.var);
		ZVAL_NULL(arg);
		ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
	}

	arg = ZEND_CALL_VAR(EX(call), opline->result.var);

	if (OP1_TYPE == IS_CV) {
		ZVAL_COPY_DEREF(arg, varptr);
	} else /* if (OP1_TYPE == IS_VAR) */ {
		if (UNEXPECTED(Z_ISREF_P(varptr))) {
			zend_refcounted *ref = Z_COUNTED_P(varptr);

			varptr = Z_REFVAL_P(varptr);
			ZVAL_COPY_VALUE(arg, varptr);
			if (UNEXPECTED(GC_DELREF(ref) == 0)) {
				efree_size(ref, sizeof(zend_reference));
			} else if (Z_OPT_REFCOUNTED_P(arg)) {
				Z_ADDREF_P(arg);
			}
		} else {
			ZVAL_COPY_VALUE(arg, varptr);
		}
	}

	ZEND_VM_NEXT_OPCODE();
}


ZEND_VM_HOT_TYPE_SPEC_HANDLER(ZEND_MUL, (op1_info == MAY_BE_LONG && op2_info == MAY_BE_LONG), ZEND_MUL_LONG, CONST|TMPVARCV, CONST|TMPVARCV, SPEC(NO_CONST_CONST,COMMUTATIVE))
{
	USE_OPLINE
	zval *op1, *op2, *result;
	zend_long overflow;

	op1 = GET_OP1_ZVAL_PTR_UNDEF(BP_VAR_R);
	op2 = GET_OP2_ZVAL_PTR_UNDEF(BP_VAR_R);
	result = EX_VAR(opline->result.var);
	ZEND_SIGNED_MULTIPLY_LONG(Z_LVAL_P(op1), Z_LVAL_P(op2), Z_LVAL_P(result), Z_DVAL_P(result), overflow);
	Z_TYPE_INFO_P(result) = overflow ? IS_DOUBLE : IS_LONG;
	ZEND_VM_NEXT_OPCODE();
}


ZEND_VM_HANDLER(119, ZEND_SEND_ARRAY, ANY, ANY, NUM)
{
	USE_OPLINE
	zend_free_op free_op1;
	zval *args;

	SAVE_OPLINE();
	args = GET_OP1_ZVAL_PTR(BP_VAR_R);

	if (UNEXPECTED(Z_TYPE_P(args) != IS_ARRAY)) {
		if ((OP1_TYPE & (IS_VAR|IS_CV)) && Z_ISREF_P(args)) {
			args = Z_REFVAL_P(args);
			if (EXPECTED(Z_TYPE_P(args) == IS_ARRAY)) {
				ZEND_VM_C_GOTO(send_array);
			}
		}
		zend_internal_type_error(EX_USES_STRICT_TYPES(), "call_user_func_array() expects parameter 2 to be array, %s given", zend_get_type_by_const(Z_TYPE_P(args)));
		if (ZEND_CALL_INFO(EX(call)) & ZEND_CALL_CLOSURE) {
			OBJ_RELEASE(ZEND_CLOSURE_OBJECT(EX(call)->func));
		} else if (ZEND_CALL_INFO(EX(call)) & ZEND_CALL_RELEASE_THIS) {
			OBJ_RELEASE(Z_OBJ(EX(call)->This));
		}
		EX(call)->func = (zend_function*)&zend_pass_function;
		Z_OBJ(EX(call)->This) = NULL;
		ZEND_CALL_INFO(EX(call)) &= ~(ZEND_CALL_RELEASE_THIS | ZEND_CALL_HAS_THIS);
		FREE_UNFETCHED_OP2();
	} else {
		uint32_t arg_num;
		HashTable *ht;
		zval *arg, *param;

ZEND_VM_C_LABEL(send_array):
		ht = Z_ARRVAL_P(args);
		if (OP2_TYPE != IS_UNUSED) {
			zend_free_op free_op2;
			zval *op2 = GET_OP2_ZVAL_PTR(BP_VAR_R);
			uint32_t skip = opline->extended_value;
			uint32_t count = zend_hash_num_elements(ht);
			zend_long len = zval_get_long(op2);

			if (len < 0) {
				len += (zend_long)(count - skip);
			}
			if (skip < count && len > 0) {
				if (len > (zend_long)(count - skip)) {
					len = (zend_long)(count - skip);
				}
				zend_vm_stack_extend_call_frame(&EX(call), 0, len);
				arg_num = 1;
				param = ZEND_CALL_ARG(EX(call), 1);
				ZEND_HASH_FOREACH_VAL(ht, arg) {
					zend_bool must_wrap = 0;
					if (skip > 0) {
						skip--;
						continue;
					} else if ((zend_long)(arg_num - 1) >= len) {
						break;
					} else if (ARG_SHOULD_BE_SENT_BY_REF(EX(call)->func, arg_num)) {
						if (UNEXPECTED(!Z_ISREF_P(arg))) {
							if (!ARG_MAY_BE_SENT_BY_REF(EX(call)->func, arg_num)) {
								/* By-value send is not allowed -- emit a warning,
								 * but still perform the call. */
								zend_param_must_be_ref(EX(call)->func, arg_num);
								must_wrap = 1;
							}
						}
					} else {
						if (Z_ISREF_P(arg) &&
						    !(EX(call)->func->common.fn_flags & ZEND_ACC_CALL_VIA_TRAMPOLINE)) {
							/* don't separate references for __call */
							arg = Z_REFVAL_P(arg);
						}
					}
					if (EXPECTED(!must_wrap)) {
						ZVAL_COPY(param, arg);
					} else {
						Z_TRY_ADDREF_P(arg);
						ZVAL_NEW_REF(param, arg);
					}
					ZEND_CALL_NUM_ARGS(EX(call))++;
					arg_num++;
					param++;
				} ZEND_HASH_FOREACH_END();
			}
			FREE_OP2();
		} else {
			zend_vm_stack_extend_call_frame(&EX(call), 0, zend_hash_num_elements(ht));
			arg_num = 1;
			param = ZEND_CALL_ARG(EX(call), 1);
			ZEND_HASH_FOREACH_VAL(ht, arg) {
				zend_bool must_wrap = 0;
				if (ARG_SHOULD_BE_SENT_BY_REF(EX(call)->func, arg_num)) {
					if (UNEXPECTED(!Z_ISREF_P(arg))) {
						if (!ARG_MAY_BE_SENT_BY_REF(EX(call)->func, arg_num)) {
							/* By-value send is not allowed -- emit a warning,
							 * but still perform the call. */
							zend_param_must_be_ref(EX(call)->func, arg_num);
							must_wrap = 1;
						}
					}
				} else {
					if (Z_ISREF_P(arg) &&
					    !(EX(call)->func->common.fn_flags & ZEND_ACC_CALL_VIA_TRAMPOLINE)) {
						/* don't separate references for __call */
						arg = Z_REFVAL_P(arg);
					}
				}
				if (EXPECTED(!must_wrap)) {
					ZVAL_COPY(param, arg);
				} else {
					Z_TRY_ADDREF_P(arg);
					ZVAL_NEW_REF(param, arg);
				}
				ZEND_CALL_NUM_ARGS(EX(call))++;
				arg_num++;
				param++;
			} ZEND_HASH_FOREACH_END();
		}
	}
	FREE_OP1();
	ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
}


ZEND_VM_HANDLER(184, ZEND_FETCH_THIS, UNUSED, UNUSED)
{
	USE_OPLINE

	if (EXPECTED(Z_TYPE(EX(This)) == IS_OBJECT)) {
		zval *result = EX_VAR(opline->result.var);

		ZVAL_OBJ(result, Z_OBJ(EX(This)));
		Z_ADDREF_P(result);
		ZEND_VM_NEXT_OPCODE();
	} else {
		ZEND_VM_DISPATCH_TO_HELPER(zend_this_not_in_object_context_helper);
	}
}


ZEND_VM_COLD_HELPER(zend_mod_by_zero_helper, ANY, ANY)
{
	USE_OPLINE

	SAVE_OPLINE();
	zend_throw_exception_ex(zend_ce_division_by_zero_error, 0, "Modulo by zero");
	ZVAL_UNDEF(EX_VAR(opline->result.var));
	HANDLE_EXCEPTION();
}


ZEND_VM_HOT_OBJ_HANDLER(82, ZEND_FETCH_OBJ_R, CONST|TMPVAR|UNUSED|THIS|CV, CONST|TMPVAR|CV, CACHE_SLOT)
{
	USE_OPLINE
	zend_free_op free_op1;
	zval *container;
	zend_free_op free_op2;
	zval *offset;
	void **cache_slot = NULL;

	SAVE_OPLINE();
	container = GET_OP1_OBJ_ZVAL_PTR_UNDEF(BP_VAR_R);

	if (OP1_TYPE == IS_UNUSED && UNEXPECTED(Z_TYPE_P(container) == IS_UNDEF)) {
		ZEND_VM_DISPATCH_TO_HELPER(zend_this_not_in_object_context_helper);
	}

	offset = GET_OP2_ZVAL_PTR_UNDEF(BP_VAR_R);

	if (OP1_TYPE == IS_CONST ||
	    (OP1_TYPE != IS_UNUSED && UNEXPECTED(Z_TYPE_P(container) != IS_OBJECT))) {
	    do {
			if ((OP1_TYPE & (IS_VAR|IS_CV)) && Z_ISREF_P(container)) {
				container = Z_REFVAL_P(container);
				if (EXPECTED(Z_TYPE_P(container) == IS_OBJECT)) {
					break;
				}
			}
			if (OP1_TYPE == IS_CV && UNEXPECTED(Z_TYPE_P(container) == IS_UNDEF)) {
				ZVAL_UNDEFINED_OP1();
			}
			if (OP2_TYPE == IS_CV && UNEXPECTED(Z_TYPE_P(offset) == IS_UNDEF)) {
				ZVAL_UNDEFINED_OP2();
			}
			zend_wrong_property_read(offset);
			ZVAL_NULL(EX_VAR(opline->result.var));
			ZEND_VM_C_GOTO(fetch_obj_r_finish);
		} while (0);
	}

	/* here we are sure we are dealing with an object */
	do {
		zend_object *zobj = Z_OBJ_P(container);
		zval *retval;

		if (OP2_TYPE == IS_CONST) {
			cache_slot = CACHE_ADDR(opline->extended_value & ~ZEND_FETCH_REF /* FUNC_ARG fetch may contain it */);

			if (EXPECTED(zobj->ce == CACHED_PTR_EX(cache_slot))) {
				uintptr_t prop_offset = (uintptr_t)CACHED_PTR_EX(cache_slot + 1);

				if (EXPECTED(IS_VALID_PROPERTY_OFFSET(prop_offset))) {
					retval = OBJ_PROP(zobj, prop_offset);
					if (EXPECTED(Z_TYPE_INFO_P(retval) != IS_UNDEF)) {
						if (!ZEND_VM_SPEC || (OP1_TYPE & (IS_TMP_VAR|IS_VAR)) != 0) {
							ZEND_VM_C_GOTO(fetch_obj_r_copy);
						} else {
ZEND_VM_C_LABEL(fetch_obj_r_fast_copy):
							ZVAL_COPY_DEREF(EX_VAR(opline->result.var), retval);
							ZEND_VM_NEXT_OPCODE();
						}
					}
				} else if (EXPECTED(zobj->properties != NULL)) {
					if (!IS_UNKNOWN_DYNAMIC_PROPERTY_OFFSET(prop_offset)) {
						uintptr_t idx = ZEND_DECODE_DYN_PROP_OFFSET(prop_offset);

						if (EXPECTED(idx < zobj->properties->nNumUsed * sizeof(Bucket))) {
							Bucket *p = (Bucket*)((char*)zobj->properties->arData + idx);

							if (EXPECTED(Z_TYPE(p->val) != IS_UNDEF) &&
						        (EXPECTED(p->key == Z_STR_P(offset)) ||
						         (EXPECTED(p->h == ZSTR_H(Z_STR_P(offset))) &&
						          EXPECTED(p->key != NULL) &&
						          EXPECTED(zend_string_equal_content(p->key, Z_STR_P(offset)))))) {
								retval = &p->val;
								if (!ZEND_VM_SPEC || (OP1_TYPE & (IS_TMP_VAR|IS_VAR)) != 0) {
									ZEND_VM_C_GOTO(fetch_obj_r_copy);
								} else {
									ZEND_VM_C_GOTO(fetch_obj_r_fast_copy);
								}
							}
						}
						CACHE_PTR_EX(cache_slot + 1, (void*)ZEND_DYNAMIC_PROPERTY_OFFSET);
					}
					retval = zend_hash_find_ex(zobj->properties, Z_STR_P(offset), 1);
					if (EXPECTED(retval)) {
						uintptr_t idx = (char*)retval - (char*)zobj->properties->arData;
						CACHE_PTR_EX(cache_slot + 1, (void*)ZEND_ENCODE_DYN_PROP_OFFSET(idx));
						if (!ZEND_VM_SPEC || (OP1_TYPE & (IS_TMP_VAR|IS_VAR)) != 0) {
							ZEND_VM_C_GOTO(fetch_obj_r_copy);
						} else {
							ZEND_VM_C_GOTO(fetch_obj_r_fast_copy);
						}
					}
				}
			}
		} else if (OP2_TYPE == IS_CV && UNEXPECTED(Z_TYPE_INFO_P(offset) == IS_UNDEF)) {
			ZVAL_UNDEFINED_OP2();
		}

		retval = zobj->handlers->read_property(container, offset, BP_VAR_R, cache_slot, EX_VAR(opline->result.var));

		if (retval != EX_VAR(opline->result.var)) {
ZEND_VM_C_LABEL(fetch_obj_r_copy):
			ZVAL_COPY_DEREF(EX_VAR(opline->result.var), retval);
		} else if (UNEXPECTED(Z_ISREF_P(retval))) {
			zend_unwrap_reference(retval);
		}
	} while (0);

ZEND_VM_C_LABEL(fetch_obj_r_finish):
	FREE_OP2();
	FREE_OP1();
	ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
}


ZEND_VM_HANDLER(132, ZEND_PRE_INC_OBJ, VAR|UNUSED|THIS|CV, CONST|TMPVAR|CV, CACHE_SLOT)
{
	USE_OPLINE
	zend_free_op free_op1, free_op2;
	zval *object;
	zval *property;
	zval *zptr;
	void **cache_slot;
	zend_property_info *prop_info;

	SAVE_OPLINE();
	object = GET_OP1_OBJ_ZVAL_PTR_PTR_UNDEF(BP_VAR_RW);

	if (OP1_TYPE == IS_UNUSED && UNEXPECTED(Z_TYPE_P(object) == IS_UNDEF)) {
		ZEND_VM_DISPATCH_TO_HELPER(zend_this_not_in_object_context_helper);
	}

	property = GET_OP2_ZVAL_PTR(BP_VAR_R);

	do {
		if (OP1_TYPE != IS_UNUSED && UNEXPECTED(Z_TYPE_P(object) != IS_OBJECT)) {
			if (Z_ISREF_P(object) && Z_TYPE_P(Z_REFVAL_P(object)) == IS_OBJECT) {
				object = Z_REFVAL_P(object);
				ZEND_VM_C_GOTO(pre_incdec_object);
			}
			if (OP1_TYPE == IS_CV
			 && UNEXPECTED(Z_TYPE_P(object) == IS_UNDEF)) {
				ZVAL_UNDEFINED_OP1();
			}
			object = make_real_object(object, property OPLINE_CC EXECUTE_DATA_CC);
			if (UNEXPECTED(!object)) {
				break;
			}
		}

ZEND_VM_C_LABEL(pre_incdec_object):
		/* here we are sure we are dealing with an object */
		cache_slot = (OP2_TYPE == IS_CONST) ? CACHE_ADDR(opline->extended_value) : NULL;
		if (EXPECTED((zptr = Z_OBJ_HT_P(object)->get_property_ptr_ptr(object, property, BP_VAR_RW, cache_slot)) != NULL)) {
			if (UNEXPECTED(Z_ISERROR_P(zptr))) {
				if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
					ZVAL_NULL(EX_VAR(opline->result.var));
				}
			} else {
				if (OP2_TYPE == IS_CONST) {
					prop_info = (zend_property_info *) CACHED_PTR_EX(cache_slot + 2);
				} else {
					prop_info = zend_object_fetch_property_type_info(Z_OBJ_P(object), zptr);
				}
				zend_pre_incdec_property_zval(zptr, prop_info OPLINE_CC EXECUTE_DATA_CC);
			}
		} else {
			zend_pre_incdec_overloaded_property(object, property, cache_slot OPLINE_CC EXECUTE_DATA_CC);
		}
	} while (0);

	FREE_OP2();
	FREE_OP1_VAR_PTR();
	ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
}


ZEND_API int ZEND_FASTCALL bitwise_xor_function(zval *result, zval *op1, zval *op2) /* {{{ */
{
	zend_long op1_lval, op2_lval;

	if (EXPECTED(Z_TYPE_P(op1) == IS_LONG) && EXPECTED(Z_TYPE_P(op2) == IS_LONG)) {
		ZVAL_LONG(result, Z_LVAL_P(op1) ^ Z_LVAL_P(op2));
		return SUCCESS;
	}

	ZVAL_DEREF(op1);
	ZVAL_DEREF(op2);

	if (Z_TYPE_P(op1) == IS_STRING && Z_TYPE_P(op2) == IS_STRING) {
		zval *longer, *shorter;
		zend_string *str;
		size_t i;

		if (EXPECTED(Z_STRLEN_P(op1) >= Z_STRLEN_P(op2))) {
			if (EXPECTED(Z_STRLEN_P(op1) == Z_STRLEN_P(op2)) && Z_STRLEN_P(op1) == 1) {
				zend_uchar xor = (zend_uchar) (*Z_STRVAL_P(op1) ^ *Z_STRVAL_P(op2));
				if (result==op1) {
					zval_ptr_dtor_str(result);
				}
				ZVAL_INTERNED_STR(result, ZSTR_CHAR(xor));
				return SUCCESS;
			}
			longer = op1;
			shorter = op2;
		} else {
			longer = op2;
			shorter = op1;
		}

		str = zend_string_alloc(Z_STRLEN_P(shorter), 0);
		for (i = 0; i < Z_STRLEN_P(shorter); i++) {
			ZSTR_VAL(str)[i] = Z_STRVAL_P(shorter)[i] ^ Z_STRVAL_P(longer)[i];
		}
		ZSTR_VAL(str)[i] = 0;
		if (result==op1) {
			zval_ptr_dtor_str(result);
		}
		ZVAL_NEW_STR(result, str);
		return SUCCESS;
	}

	if (UNEXPECTED(Z_TYPE_P(op1) != IS_LONG)) {
		ZEND_TRY_BINARY_OP1_OBJECT_OPERATION(ZEND_BW_XOR, bitwise_xor_function);
		op1_lval = _zval_get_long_func_noisy(op1);
		if (UNEXPECTED(EG(exception))) {
			if (result != op1) {
				ZVAL_UNDEF(result);
			}
			return FAILURE;
		}
	} else {
		op1_lval = Z_LVAL_P(op1);
	}
	if (UNEXPECTED(Z_TYPE_P(op2) != IS_LONG)) {
		ZEND_TRY_BINARY_OP2_OBJECT_OPERATION(ZEND_BW_XOR);
		op2_lval = _zval_get_long_func_noisy(op2);
		if (UNEXPECTED(EG(exception))) {
			if (result != op1) {
				ZVAL_UNDEF(result);
			}
			return FAILURE;
		}
	} else {
		op2_lval = Z_LVAL_P(op2);
	}

	if (op1 == result) {
		zval_ptr_dtor(result);
	}
	ZVAL_LONG(result, op1_lval ^ op2_lval);
	return SUCCESS;
}


ZEND_API void ZEND_FASTCALL convert_to_long(zval *op) /* {{{ */
{
	if (Z_TYPE_P(op) != IS_LONG) {
		convert_to_long_base(op, 10);
	}
}


ZEND_API int ZEND_FASTCALL zend_binary_strncasecmp(const char *s1, size_t len1, const char *s2, size_t len2, size_t length) /* {{{ */
{
	size_t len;
	int c1, c2;

	if (s1 == s2) {
		return 0;
	}
	len = MIN(length, MIN(len1, len2));
	while (len--) {
		c1 = zend_tolower_ascii(*(unsigned char *)s1++);
		c2 = zend_tolower_ascii(*(unsigned char *)s2++);
		if (c1 != c2) {
			return c1 - c2;
		}
	}

	return (int)(MIN(length, len1) - MIN(length, len2));
}


ZEND_VM_HANDLER(83, ZEND_FETCH_W, CONST|TMPVAR|CV, UNUSED, VAR_FETCH)
{
	ZEND_VM_DISPATCH_TO_HELPER(zend_fetch_var_address_helper, type, BP_VAR_W);
}


ZEND_VM_HOT_TYPE_SPEC_HANDLER(ZEND_ADD, (op1_info == MAY_BE_DOUBLE && op2_info == MAY_BE_DOUBLE), ZEND_ADD_DOUBLE, CONST|TMPVARCV, CONST|TMPVARCV, SPEC(NO_CONST_CONST,COMMUTATIVE))
{
	USE_OPLINE
	zval *op1, *op2, *result;

	op1 = GET_OP1_ZVAL_PTR_UNDEF(BP_VAR_R);
	op2 = GET_OP2_ZVAL_PTR_UNDEF(BP_VAR_R);
	result = EX_VAR(opline->result.var);
	ZVAL_DOUBLE(result, Z_DVAL_P(op1) + Z_DVAL_P(op2));
	ZEND_VM_NEXT_OPCODE();
}


ZEND_API void zend_update_current_locale(void) /* {{{ */
{
	current_locale = _get_current_locale();
}


ZEND_VM_COLD_HELPER(zend_this_not_in_object_context_helper, ANY, ANY)
{
	USE_OPLINE

	SAVE_OPLINE();
	zend_throw_error(NULL, "Using $this when not in object context");
	if ((opline+1)->opcode == ZEND_OP_DATA) {
		FREE_UNFETCHED_OP_DATA();
	}
	FREE_UNFETCHED_OP2();
	UNDEF_RESULT();
	HANDLE_EXCEPTION();
}


ZEND_VM_HOT_TYPE_SPEC_HANDLER(ZEND_SEND_VAR_EX, op->op2.num <= MAX_ARG_FLAG_NUM && (op1_info & (MAY_BE_UNDEF|MAY_BE_REF)) == 0, ZEND_SEND_VAR_EX_SIMPLE, CV|VAR, NUM)
{
	USE_OPLINE
	zval *varptr, *arg;
	zend_free_op free_op1;
	uint32_t arg_num = opline->op2.num;

	if (QUICK_ARG_SHOULD_BE_SENT_BY_REF(EX(call)->func, arg_num)) {
		ZEND_VM_DISPATCH_TO_HANDLER(ZEND_SEND_REF);
	}

	varptr = GET_OP1_ZVAL_PTR_UNDEF(BP_VAR_R);
	arg = ZEND_CALL_VAR(EX(call), opline->result.var);

	if (OP1_TYPE == IS_CV) {
		ZVAL_COPY(arg, varptr);
	} else /* if (OP1_TYPE == IS_VAR) */ {
		ZVAL_COPY_VALUE(arg, varptr);
	}

	ZEND_VM_NEXT_OPCODE();
}


ZEND_VM_HOT_HANDLER(37, ZEND_POST_DEC, VAR|CV, ANY)
{
	USE_OPLINE
	zend_free_op free_op1;
	zval *var_ptr;

	var_ptr = GET_OP1_ZVAL_PTR_PTR_UNDEF(BP_VAR_RW);

	if (EXPECTED(Z_TYPE_P(var_ptr) == IS_LONG)) {
		ZVAL_LONG(EX_VAR(opline->result.var), Z_LVAL_P(var_ptr));
		fast_long_decrement_function(var_ptr);
		ZEND_VM_NEXT_OPCODE();
	}

	ZEND_VM_DISPATCH_TO_HELPER(zend_post_dec_helper);
}


ZEND_API int ZEND_FASTCALL is_not_equal_function(zval *result, zval *op1, zval *op2) /* {{{ */
{
	if (compare_function(result, op1, op2) == FAILURE) {
		return FAILURE;
	}
	ZVAL_BOOL(result, (Z_LVAL_P(result) != 0));
	return SUCCESS;
}


ZEND_API int ZEND_FASTCALL zend_binary_strncmp(const char *s1, size_t len1, const char *s2, size_t len2, size_t length) /* {{{ */
{
	int retval;

	if (s1 == s2) {
		return 0;
	}
	retval = memcmp(s1, s2, MIN(length, MIN(len1, len2)));
	if (!retval) {
		return (int)(MIN(length, len1) - MIN(length, len2));
	} else {
		return retval;
	}
}


ZEND_VM_COLD_CONSTCONST_HANDLER(4, ZEND_DIV, CONST|TMPVAR|CV, CONST|TMPVAR|CV)
{
	USE_OPLINE
	zend_free_op free_op1, free_op2;
	zval *op1, *op2;

	SAVE_OPLINE();
	op1 = GET_OP1_ZVAL_PTR(BP_VAR_R);
	op2 = GET_OP2_ZVAL_PTR(BP_VAR_R);
	fast_div_function(EX_VAR(opline->result.var), op1, op2);
	FREE_OP1();
	FREE_OP2();
	ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
}


ZEND_VM_HANDLER(162, ZEND_FAST_CALL, JMP_ADDR, ANY)
{
	USE_OPLINE
	zval *fast_call = EX_VAR(opline->result.var);

	Z_OBJ_P(fast_call) = NULL;
	/* set return address */
	Z_OPLINE_NUM_P(fast_call) = opline - EX(func)->op_array.opcodes;
	ZEND_VM_JMP_EX(OP_JMP_ADDR(opline, opline->op1), 0);
}


ZEND_VM_COLD_CONST_HANDLER(125, ZEND_FE_RESET_RW, CONST|TMP|VAR|CV, JMP_ADDR)
{
	USE_OPLINE
	zend_free_op free_op1;
	zval *array_ptr, *array_ref;

	SAVE_OPLINE();

	if (OP1_TYPE == IS_VAR || OP1_TYPE == IS_CV) {
		array_ref = array_ptr = GET_OP1_ZVAL_PTR_PTR(BP_VAR_R);
		if (Z_ISREF_P(array_ref)) {
			array_ptr = Z_REFVAL_P(array_ref);
		}
	} else {
		array_ref = array_ptr = GET_OP1_ZVAL_PTR(BP_VAR_R);
	}

	if (EXPECTED(Z_TYPE_P(array_ptr) == IS_ARRAY)) {
		if (OP1_TYPE == IS_VAR || OP1_TYPE == IS_CV) {
			if (array_ptr == array_ref) {
				ZVAL_NEW_REF(array_ref, array_ref);
				array_ptr = Z_REFVAL_P(array_ref);
			}
			Z_ADDREF_P(array_ref);
			ZVAL_COPY_VALUE(EX_VAR(opline->result.var), array_ref);
		} else {
			array_ref = EX_VAR(opline->result.var);
			ZVAL_NEW_REF(array_ref, array_ptr);
			array_ptr = Z_REFVAL_P(array_ref);
		}
		if (OP1_TYPE == IS_CONST) {
			ZVAL_ARR(array_ptr, zend_array_dup(Z_ARRVAL_P(array_ptr)));
		} else {
			SEPARATE_ARRAY(array_ptr);
		}
		Z_FE_ITER_P(EX_VAR(opline->result.var)) = zend_hash_iterator_add(Z_ARRVAL_P(array_ptr), 0);

		if (OP1_TYPE == IS_VAR) {
			FREE_OP1_VAR_PTR();
		}
		ZEND_VM_NEXT_OPCODE();
	} else if (OP1_TYPE != IS_CONST && EXPECTED(Z_TYPE_P(array_ptr) == IS_OBJECT)) {
		if (!Z_OBJCE_P(array_ptr)->get_iterator) {
			HashTable *properties;
			if (OP1_TYPE == IS_VAR || OP1_TYPE == IS_CV) {
				if (array_ptr == array_ref) {
					ZVAL_NEW_REF(array_ref, array_ref);
					array_ptr = Z_REFVAL_P(array_ref);
				}
				Z_ADDREF_P(array_ref);
				ZVAL_COPY_VALUE(EX_VAR(opline->result.var), array_ref);
			} else {
				array_ptr = EX_VAR(opline->result.var);
				ZVAL_COPY_VALUE(array_ptr, array_ref);
			}
			if (Z_OBJ_P(array_ptr)->properties
			 && UNEXPECTED(GC_REFCOUNT(Z_OBJ_P(array_ptr)->properties) > 1)) {
				if (EXPECTED(!(GC_FLAGS(Z_OBJ_P(array_ptr)->properties) & IS_ARRAY_IMMUTABLE))) {
					GC_DELREF(Z_OBJ_P(array_ptr)->properties);
				}
				Z_OBJ_P(array_ptr)->properties = zend_array_dup(Z_OBJ_P(array_ptr)->properties);
			}

			properties = Z_OBJPROP_P(array_ptr);
			if (zend_hash_num_elements(properties) == 0) {
				Z_FE_ITER_P(EX_VAR(opline->result.var)) = (uint32_t) -1;
				FREE_OP1_VAR_PTR();
				ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2));
			}

			Z_FE_ITER_P(EX_VAR(opline->result.var)) = zend_hash_iterator_add(properties, 0);
			FREE_OP1_VAR_PTR();
			ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
		} else {
			zend_bool is_empty = zend_fe_reset_iterator(array_ptr, 1 OPLINE_CC EXECUTE_DATA_CC);

			if (OP1_TYPE == IS_VAR) {
				FREE_OP1_VAR_PTR();
			} else {
				FREE_OP1();
			}
			if (UNEXPECTED(EG(exception))) {
				HANDLE_EXCEPTION();
			} else if (is_empty) {
				ZEND_VM_JMP_EX(OP_JMP_ADDR(opline, opline->op2), 0);
			} else {
				ZEND_VM_NEXT_OPCODE();
			}
		}
	} else {
		zend_error(E_WARNING, "Invalid argument supplied for foreach()");
		ZVAL_UNDEF(EX_VAR(opline->result.var));
		Z_FE_ITER_P(EX_VAR(opline->result.var)) = (uint32_t)-1;
		if (OP1_TYPE == IS_VAR) {
			FREE_OP1_VAR_PTR();
		} else {
			FREE_OP1();
		}
		ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2));
	}
}


ZEND_VM_HANDLER(157, ZEND_FETCH_CLASS_NAME, UNUSED|CLASS_FETCH, ANY)
{
	uint32_t fetch_type;
	zend_class_entry *called_scope, *scope;
	USE_OPLINE

	fetch_type = opline->op1.num;

	scope = EX(func)->op_array.scope;
	if (UNEXPECTED(scope == NULL)) {
		SAVE_OPLINE();
		zend_throw_error(NULL, "Cannot use \"%s\" when no class scope is active",
			fetch_type == ZEND_FETCH_CLASS_SELF ? "self" :
			fetch_type == ZEND_FETCH_CLASS_PARENT ? "parent" : "static");
		ZVAL_UNDEF(EX_VAR(opline->result.var));
		HANDLE_EXCEPTION();
	}

	switch (fetch_type) {
		case ZEND_FETCH_CLASS_SELF:
			ZVAL_STR_COPY(EX_VAR(opline->result.var), scope->name);
			break;
		case ZEND_FETCH_CLASS_PARENT:
			if (UNEXPECTED(scope->parent == NULL)) {
				SAVE_OPLINE();
				zend_throw_error(NULL,
					"Cannot use \"parent\" when current class scope has no parent");
				ZVAL_UNDEF(EX_VAR(opline->result.var));
				HANDLE_EXCEPTION();
			}
			ZVAL_STR_COPY(EX_VAR(opline->result.var), scope->parent->name);
			break;
		case ZEND_FETCH_CLASS_STATIC:
			if (Z_TYPE(EX(This)) == IS_OBJECT) {
				called_scope = Z_OBJCE(EX(This));
			} else {
				called_scope = Z_CE(EX(This));
			}
			ZVAL_STR_COPY(EX_VAR(opline->result.var), called_scope->name);
			break;
		EMPTY_SWITCH_DEFAULT_CASE()
	}
	ZEND_VM_NEXT_OPCODE();
}


ZEND_VM_HANDLER(159, ZEND_DISCARD_EXCEPTION, ANY, ANY)
{
	USE_OPLINE
	zval *fast_call = EX_VAR(opline->op1.var);
	SAVE_OPLINE();

	/* cleanup incomplete RETURN statement */
	if (Z_OPLINE_NUM_P(fast_call) != (uint32_t)-1
	 && (EX(func)->op_array.opcodes[Z_OPLINE_NUM_P(fast_call)].op2_type & (IS_TMP_VAR | IS_VAR))) {
		zval *return_value = EX_VAR(EX(func)->op_array.opcodes[Z_OPLINE_NUM_P(fast_call)].op2.var);

		zval_ptr_dtor(return_value);
	}

	/* cleanup delayed exception */
	if (Z_OBJ_P(fast_call) != NULL) {
		/* discard the previously thrown exception */
		OBJ_RELEASE(Z_OBJ_P(fast_call));
		Z_OBJ_P(fast_call) = NULL;
	}

	ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
}


ZEND_VM_HANDLER(22, ZEND_ASSIGN, VAR|CV, CONST|TMP|VAR|CV, SPEC(RETVAL))
{
	USE_OPLINE
	zend_free_op free_op1, free_op2;
	zval *value;
	zval *variable_ptr;

	SAVE_OPLINE();
	value = GET_OP2_ZVAL_PTR(BP_VAR_R);
	variable_ptr = GET_OP1_ZVAL_PTR_PTR_UNDEF(BP_VAR_W);

	if (OP1_TYPE == IS_VAR && UNEXPECTED(Z_ISERROR_P(variable_ptr))) {
		FREE_OP2();
		if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
			ZVAL_NULL(EX_VAR(opline->result.var));
		}
	} else {
		value = zend_assign_to_variable(variable_ptr, value, OP2_TYPE, EX_USES_STRICT_TYPES());
		if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
			ZVAL_COPY(EX_VAR(opline->result.var), value);
		}
		FREE_OP1_VAR_PTR();
		/* zend_assign_to_variable() always takes care of op2, never free it! */
	}

	ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
}


ZEND_VM_HANDLER(23, ZEND_ASSIGN_DIM, VAR|CV, CONST|TMPVAR|UNUSED|NEXT|CV, SPEC(OP_DATA=CONST|TMP|VAR|CV))
{
	USE_OPLINE
	zend_free_op free_op1;
	zval *object_ptr, *orig_object_ptr;
	zend_free_op free_op2, free_op_data;
	zval *value;
	zval *variable_ptr;
	zval *dim;

	SAVE_OPLINE();
	orig_object_ptr = object_ptr = GET_OP1_ZVAL_PTR_PTR_UNDEF(BP_VAR_W);

	if (EXPECTED(Z_TYPE_P(object_ptr) == IS_ARRAY)) {
ZEND_VM_C_LABEL(try_assign_dim_array):
		value = GET_OP_DATA_ZVAL_PTR(BP_VAR_R);
		SEPARATE_ARRAY(object_ptr);
		if (OP2_TYPE == IS_UNUSED) {
			if (OP_DATA_TYPE == IS_CV || OP_DATA_TYPE == IS_VAR) {
				ZVAL_DEREF(value);
			}
			variable_ptr = zend_hash_next_index_insert(Z_ARRVAL_P(object_ptr), value);
			if (UNEXPECTED(variable_ptr == NULL)) {
				zend_cannot_add_element();
				ZEND_VM_C_GOTO(assign_dim_error);
			} else if (OP_DATA_TYPE == IS_CV) {
				if (Z_REFCOUNTED_P(value)) {
					Z_ADDREF_P(value);
				}
			} else if (OP_DATA_TYPE == IS_VAR) {
				if (value != free_op_data) {
					if (Z_REFCOUNTED_P(value)) {
						Z_ADDREF_P(value);
					}
					FREE_OP_DATA();
				}
			} else if (OP_DATA_TYPE == IS_CONST) {
				if (UNEXPECTED(Z_REFCOUNTED_P(value))) {
					Z_ADDREF_P(value);
				}
			}
		} else {
			dim = GET_OP2_ZVAL_PTR_UNDEF(BP_VAR_R);
			if (OP2_TYPE == IS_CONST) {
				variable_ptr = zend_fetch_dimension_address_inner_W_CONST(Z_ARRVAL_P(object_ptr), dim EXECUTE_DATA_CC);
			} else {
				variable_ptr = zend_fetch_dimension_address_inner_W(Z_ARRVAL_P(object_ptr), dim EXECUTE_DATA_CC);
			}
			if (UNEXPECTED(variable_ptr == NULL)) {
				ZEND_VM_C_GOTO(assign_dim_error);
			}
			value = zend_assign_to_variable(variable_ptr, value, OP_DATA_TYPE, EX_USES_STRICT_TYPES());
		}
		if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
			ZVAL_COPY(EX_VAR(opline->result.var), value);
		}
	} else {
		if (EXPECTED(Z_ISREF_P(object_ptr))) {
			object_ptr = Z_REFVAL_P(object_ptr);
			if (EXPECTED(Z_TYPE_P(object_ptr) == IS_ARRAY)) {
				ZEND_VM_C_GOTO(try_assign_dim_array);
			}
		}
		if (EXPECTED(Z_TYPE_P(object_ptr) == IS_OBJECT)) {
			dim = GET_OP2_ZVAL_PTR(BP_VAR_R);
			value = GET_OP_DATA_ZVAL_PTR_DEREF(BP_VAR_R);

			if (OP2_TYPE == IS_CONST && Z_EXTRA_P(dim) == ZEND_EXTRA_VALUE) {
				dim++;
			}
			zend_assign_to_object_dim(object_ptr, dim, value OPLINE_CC EXECUTE_DATA_CC);

			FREE_OP_DATA();
		} else if (EXPECTED(Z_TYPE_P(object_ptr) == IS_STRING)) {
			if (OP2_TYPE == IS_UNUSED) {
				zend_use_new_element_for_string();
				FREE_UNFETCHED_OP_DATA();
				UNDEF_RESULT();
			} else {
				dim = GET_OP2_ZVAL_PTR(BP_VAR_R);
				value = GET_OP_DATA_ZVAL_PTR_DEREF(BP_VAR_R);
				zend_assign_to_string_offset(object_ptr, dim, value OPLINE_CC EXECUTE_DATA_CC);
				FREE_OP_DATA();
			}
		} else if (EXPECTED(Z_TYPE_P(object_ptr) <= IS_FALSE)) {
			if (Z_ISREF_P(orig_object_ptr)
			 && ZEND_REF_HAS_TYPE_SOURCES(Z_REF_P(orig_object_ptr))
			 && !zend_verify_ref_array_assignable(Z_REF_P(orig_object_ptr))) {
				dim = GET_OP2_ZVAL_PTR(BP_VAR_R);
				FREE_UNFETCHED_OP_DATA();
				UNDEF_RESULT();
			} else {
				ZVAL_ARR(object_ptr, zend_new_array(8));
				ZEND_VM_C_GOTO(try_assign_dim_array);
			}
		} else {
			if (OP1_TYPE != IS_VAR || EXPECTED(!Z_ISERROR_P(object_ptr))) {
				zend_use_scalar_as_array();
			}
			dim = GET_OP2_ZVAL_PTR(BP_VAR_R);
ZEND_VM_C_LABEL(assign_dim_error):
			FREE_UNFETCHED_OP_DATA();
			if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
				ZVAL_NULL(EX_VAR(opline->result.var));
			}
		}
	}
	if (OP2_TYPE != IS_UNUSED) {
		FREE_OP2();
	}
	FREE_OP1_VAR_PTR();
	/* assign_dim has two opcodes! */
	ZEND_VM_NEXT_OPCODE_EX(1, 2);
}


ZEND_VM_HANDLER(114, ZEND_ISSET_ISEMPTY_VAR, CONST|TMPVAR|CV, UNUSED, VAR_FETCH|ISSET)
{
	USE_OPLINE
	zval *value;
	int result;
	zend_free_op free_op1;
	zval *varname;
	zend_string *name, *tmp_name;
	HashTable *target_symbol_table;

	SAVE_OPLINE();
	varname = GET_OP1_ZVAL_PTR(BP_VAR_IS);
	if (OP1_TYPE == IS_CONST) {
		name = Z_STR_P(varname);
	} else {
		name = zval_get_tmp_string(varname, &tmp_name);
	}

	target_symbol_table = zend_get_target_symbol_table(opline->extended_value EXECUTE_DATA_CC);
	value = zend_hash_find_ex(target_symbol_table, name, OP1_TYPE == IS_CONST);

	if (OP1_TYPE != IS_CONST) {
		zend_tmp_string_release(tmp_name);
	}
	FREE_OP1();

	if (!value) {
		result = (opline->extended_value & ZEND_ISEMPTY);
	} else {
		if (Z_TYPE_P(value) == IS_INDIRECT) {
			value = Z_INDIRECT_P(value);
		}
		if (!(opline->extended_value & ZEND_ISEMPTY)) {
			if (Z_ISREF_P(value)) {
				value = Z_REFVAL_P(value);
			}
			result = Z_TYPE_P(value) > IS_NULL;
		} else {
			result = !i_zend_is_true(value);
		}
	}

	ZEND_VM_SMART_BRANCH(result, 1);
	ZVAL_BOOL(EX_VAR(opline->result.var), result);
	ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
}


ZEND_VM_HANDLER(55, ZEND_ROPE_ADD, TMP, CONST|TMPVAR|CV, NUM)
{
	USE_OPLINE
	zend_free_op free_op2;
	zend_string **rope;
	zval *var;

	/* op1 and result are the same */
	rope = (zend_string**)EX_VAR(opline->op1.var);
	if (OP2_TYPE == IS_CONST) {
		var = GET_OP2_ZVAL_PTR(BP_VAR_R);
		rope[opline->extended_value] = Z_STR_P(var);
		if (UNEXPECTED(Z_REFCOUNTED_P(var))) {
			Z_ADDREF_P(var);
		}
	} else {
		var = GET_OP2_ZVAL_PTR_UNDEF(BP_VAR_R);
		if (EXPECTED(Z_TYPE_P(var) == IS_STRING)) {
			if (OP2_TYPE == IS_CV) {
				rope[opline->extended_value] = zend_string_copy(Z_STR_P(var));
			} else {
				rope[opline->extended_value] = Z_STR_P(var);
			}
		} else {
			SAVE_OPLINE();
			if (OP2_TYPE == IS_CV && UNEXPECTED(Z_TYPE_P(var) == IS_UNDEF)) {
				ZVAL_UNDEFINED_OP2();
			}
			rope[opline->extended_value] = zval_get_string_func(var);
			FREE_OP2();
			ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
		}
	}
	ZEND_VM_NEXT_OPCODE();
}


ZEND_API int ZEND_FASTCALL zend_is_true(zval *op) /* {{{ */
{
	return i_zend_is_true(op);
}


ZEND_VM_HOT_TYPE_SPEC_HANDLER(ZEND_IS_SMALLER_OR_EQUAL, (op1_info == MAY_BE_DOUBLE && op2_info == MAY_BE_DOUBLE), ZEND_IS_SMALLER_OR_EQUAL_DOUBLE, CONST|TMPVARCV, CONST|TMPVARCV, SPEC(SMART_BRANCH,NO_CONST_CONST))
{
	USE_OPLINE
	zval *op1, *op2;
	int result;

	op1 = GET_OP1_ZVAL_PTR_UNDEF(BP_VAR_R);
	op2 = GET_OP2_ZVAL_PTR_UNDEF(BP_VAR_R);
	result = (Z_DVAL_P(op1) <= Z_DVAL_P(op2));
	ZEND_VM_SMART_BRANCH(result, 0);
	ZVAL_BOOL(EX_VAR(opline->result.var), result);
	ZEND_VM_NEXT_OPCODE();
}


ZEND_API void ZEND_FASTCALL _convert_to_cstring(zval *op) /* {{{ */
{
	if (Z_TYPE_P(op) == IS_DOUBLE) {
		zend_string *str;
		double dval = Z_DVAL_P(op);

		str = zend_strpprintf_unchecked(0, "%.*H", (int) EG(precision), dval);
		ZVAL_NEW_STR(op, str);
	} else {
		_convert_to_string(op);
	}
}


ZEND_VM_HOT_NOCONST_HANDLER(123, ZEND_TYPE_CHECK, CONST|TMPVAR|CV, ANY, TYPE_MASK)
{
	USE_OPLINE
	zval *value;
	int result = 0;
	zend_free_op free_op1;

	value = GET_OP1_ZVAL_PTR_UNDEF(BP_VAR_R);
	if ((opline->extended_value >> (uint32_t)Z_TYPE_P(value)) & 1) {
ZEND_VM_C_LABEL(type_check_resource):
		if (EXPECTED(Z_TYPE_P(value) != IS_RESOURCE)
		 || EXPECTED(NULL != zend_rsrc_list_get_rsrc_type(Z_RES_P(value)))) {
			result = 1;
		}
	} else if ((OP1_TYPE & (IS_CV|IS_VAR)) && Z_ISREF_P(value)) {
		value = Z_REFVAL_P(value);
		if ((opline->extended_value >> (uint32_t)Z_TYPE_P(value)) & 1) {
			ZEND_VM_C_GOTO(type_check_resource);
		}
	} else if (OP1_TYPE == IS_CV && UNEXPECTED(Z_TYPE_P(value) == IS_UNDEF)) {
		result = ((1 << IS_NULL) & opline->extended_value) != 0;
		SAVE_OPLINE();
		ZVAL_UNDEFINED_OP1();
		if (UNEXPECTED(EG(exception))) {
			ZVAL_UNDEF(EX_VAR(opline->result.var));
			HANDLE_EXCEPTION();
		}
	}
	if (OP1_TYPE & (IS_TMP_VAR|IS_VAR)) {
		SAVE_OPLINE();
		FREE_OP1();
		ZEND_VM_SMART_BRANCH(result, 1);
		ZVAL_BOOL(EX_VAR(opline->result.var), result);
		ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
	} else {
		ZEND_VM_SMART_BRANCH(result, 0);
		ZVAL_BOOL(EX_VAR(opline->result.var), result);
		ZEND_VM_NEXT_OPCODE();
	}
}


ZEND_VM_HOT_HANDLER(129, ZEND_DO_ICALL, ANY, ANY, SPEC(RETVAL))
{
	USE_OPLINE
	zend_execute_data *call = EX(call);
	zend_function *fbc = call->func;
	zval *ret;
	zval retval;

	SAVE_OPLINE();
	EX(call) = call->prev_execute_data;

	call->prev_execute_data = execute_data;
	EG(current_execute_data) = call;

	ret = RETURN_VALUE_USED(opline) ? EX_VAR(opline->result.var) : &retval;
	ZVAL_NULL(ret);

	fbc->internal_function.handler(call, ret);

#if ZEND_DEBUG
	if (!EG(exception) && call->func) {
		ZEND_ASSERT(!(call->func->common.fn_flags & ZEND_ACC_HAS_RETURN_TYPE) ||
			zend_verify_internal_return_type(call->func, ret));
		ZEND_ASSERT((call->func->common.fn_flags & ZEND_ACC_RETURN_REFERENCE)
			? Z_ISREF_P(ret) : !Z_ISREF_P(ret));
	}
#endif

	EG(current_execute_data) = execute_data;
	zend_vm_stack_free_args(call);
	zend_vm_stack_free_call_frame(call);

	if (!RETURN_VALUE_USED(opline)) {
		i_zval_ptr_dtor(ret);
	}

	if (UNEXPECTED(EG(exception) != NULL)) {
		zend_rethrow_exception(execute_data);
		HANDLE_EXCEPTION();
	}

	ZEND_VM_SET_OPCODE(opline + 1);
	ZEND_VM_CONTINUE();
}


ZEND_VM_HANDLER(49, ZEND_CHECK_VAR, CV, UNUSED)
{
	USE_OPLINE
	zval *op1 = EX_VAR(opline->op1.var);

	if (UNEXPECTED(Z_TYPE_INFO_P(op1) == IS_UNDEF)) {
		SAVE_OPLINE();
		ZVAL_UNDEFINED_OP1();
		ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
	}
	ZEND_VM_NEXT_OPCODE();
}


ZEND_VM_HELPER(zend_post_inc_helper, VAR|CV, ANY)
{
	USE_OPLINE
	zend_free_op free_op1;
	zval *var_ptr;

	var_ptr = GET_OP1_ZVAL_PTR_PTR_UNDEF(BP_VAR_RW);

	if (OP1_TYPE == IS_VAR && UNEXPECTED(Z_ISERROR_P(var_ptr))) {
		ZVAL_NULL(EX_VAR(opline->result.var));
		ZEND_VM_NEXT_OPCODE();
	}

	SAVE_OPLINE();
	if (OP1_TYPE == IS_CV && UNEXPECTED(Z_TYPE_P(var_ptr) == IS_UNDEF)) {
		ZVAL_NULL(var_ptr);
		ZVAL_UNDEFINED_OP1();
	}

	do {
		if (UNEXPECTED(Z_TYPE_P(var_ptr) == IS_REFERENCE)) {
			zend_reference *ref = Z_REF_P(var_ptr);
			var_ptr = Z_REFVAL_P(var_ptr);

			if (UNEXPECTED(ZEND_REF_HAS_TYPE_SOURCES(ref))) {
				zend_incdec_typed_ref(ref, EX_VAR(opline->result.var) OPLINE_CC EXECUTE_DATA_CC);
				break;
			}
		}
		ZVAL_COPY(EX_VAR(opline->result.var), var_ptr);

		increment_function(var_ptr);
	} while (0);

	FREE_OP1_VAR_PTR();
	ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
}


ZEND_VM_HOT_TYPE_SPEC_HANDLER(ZEND_MUL, (op1_info == MAY_BE_DOUBLE && op2_info == MAY_BE_DOUBLE), ZEND_MUL_DOUBLE, CONST|TMPVARCV, CONST|TMPVARCV, SPEC(NO_CONST_CONST,COMMUTATIVE))
{
	USE_OPLINE
	zval *op1, *op2, *result;

	op1 = GET_OP1_ZVAL_PTR_UNDEF(BP_VAR_R);
	op2 = GET_OP2_ZVAL_PTR_UNDEF(BP_VAR_R);
	result = EX_VAR(opline->result.var);
	ZVAL_DOUBLE(result, Z_DVAL_P(op1) * Z_DVAL_P(op2));
	ZEND_VM_NEXT_OPCODE();
}


xmlValidGetElemDecl(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
	            xmlNodePtr elem, int *extsubset) {
    xmlElementPtr elemDecl = NULL;
    const xmlChar *prefix = NULL;

    if ((ctxt == NULL) || (doc == NULL) ||
        (elem == NULL) || (elem->name == NULL))
        return(NULL);
    if (extsubset != NULL)
	*extsubset = 0;

    /*
     * Fetch the declaration for the qualified name
     */
    if ((elem->ns != NULL) && (elem->ns->prefix != NULL))
	prefix = elem->ns->prefix;

    if (prefix != NULL) {
	elemDecl = xmlGetDtdQElementDesc(doc->intSubset,
		                         elem->name, prefix);
	if ((elemDecl == NULL) && (doc->extSubset != NULL)) {
	    elemDecl = xmlGetDtdQElementDesc(doc->extSubset,
		                             elem->name, prefix);
	    if ((elemDecl != NULL) && (extsubset != NULL))
		*extsubset = 1;
	}
    }

    /*
     * Fetch the declaration for the non qualified name
     * This is "non-strict" validation should be done on the
     * full QName but in that case being flexible makes sense.
     */
    if (elemDecl == NULL) {
	elemDecl = xmlGetDtdElementDesc(doc->intSubset, elem->name);
	if ((elemDecl == NULL) && (doc->extSubset != NULL)) {
	    elemDecl = xmlGetDtdElementDesc(doc->extSubset, elem->name);
	    if ((elemDecl != NULL) && (extsubset != NULL))
		*extsubset = 1;
	}
    }
    if (elemDecl == NULL) {
	xmlErrValidNode(ctxt, elem,
			XML_DTD_UNKNOWN_ELEM,
	       "No declaration for element %s\n",
	       elem->name, NULL, NULL);
    }
    return(elemDecl);
}


xmlCreateEnumeration(const xmlChar *name) {
    xmlEnumerationPtr ret;

    ret = (xmlEnumerationPtr) xmlMalloc(sizeof(xmlEnumeration));
    if (ret == NULL) {
	xmlVErrMemory(NULL, "malloc failed");
        return(NULL);
    }
    memset(ret, 0, sizeof(xmlEnumeration));

    if (name != NULL)
        ret->name = xmlStrdup(name);
    return(ret);
}


xmlValidateDtdFinal(xmlValidCtxtPtr ctxt, xmlDocPtr doc) {
    xmlDtdPtr dtd;
    xmlAttributeTablePtr table;
    xmlEntitiesTablePtr entities;

    if ((doc == NULL) || (ctxt == NULL)) return(0);
    if ((doc->intSubset == NULL) && (doc->extSubset == NULL))
	return(0);
    ctxt->doc = doc;
    ctxt->valid = 1;
    dtd = doc->intSubset;
    if ((dtd != NULL) && (dtd->attributes != NULL)) {
	table = (xmlAttributeTablePtr) dtd->attributes;
	xmlHashScan(table, (xmlHashScanner) xmlValidateAttributeCallback, ctxt);
    }
    if ((dtd != NULL) && (dtd->entities != NULL)) {
	entities = (xmlEntitiesTablePtr) dtd->entities;
	xmlHashScan(entities, (xmlHashScanner) xmlValidateNotationCallback,
		    ctxt);
    }
    dtd = doc->extSubset;
    if ((dtd != NULL) && (dtd->attributes != NULL)) {
	table = (xmlAttributeTablePtr) dtd->attributes;
	xmlHashScan(table, (xmlHashScanner) xmlValidateAttributeCallback, ctxt);
    }
    if ((dtd != NULL) && (dtd->entities != NULL)) {
	entities = (xmlEntitiesTablePtr) dtd->entities;
	xmlHashScan(entities, (xmlHashScanner) xmlValidateNotationCallback,
		    ctxt);
    }
    return(ctxt->valid);
}


xmlValidateNmtokenValue(const xmlChar *value) {
    return(xmlValidateNmtokenValueInternal(NULL, value));
}


xmlGetID(xmlDocPtr doc, const xmlChar *ID) {
    xmlIDTablePtr table;
    xmlIDPtr id;

    if (doc == NULL) {
	return(NULL);
    }

    if (ID == NULL) {
	return(NULL);
    }

    table = (xmlIDTablePtr) doc->ids;
    if (table == NULL)
        return(NULL);

    id = xmlHashLookup(table, ID);
    if (id == NULL)
	return(NULL);
    if (id->attr == NULL) {
	/*
	 * We are operating on a stream, return a well known reference
	 * since the attribute node doesn't exist anymore
	 */
	return((xmlAttrPtr) doc);
    }
    return(id->attr);
}


xmlValidateElement(xmlValidCtxtPtr ctxt, xmlDocPtr doc, xmlNodePtr elem) {
    xmlNodePtr child;
    xmlAttrPtr attr;
    xmlNsPtr ns;
    const xmlChar *value;
    int ret = 1;

    if (elem == NULL) return(0);

    /*
     * XInclude elements were added after parsing in the infoset,
     * they don't really mean anything validation wise.
     */
    if ((elem->type == XML_XINCLUDE_START) ||
	(elem->type == XML_XINCLUDE_END) ||
	(elem->type == XML_NAMESPACE_DECL))
	return(1);

    CHECK_DTD;

    /*
     * Entities references have to be handled separately
     */
    if (elem->type == XML_ENTITY_REF_NODE) {
	return(1);
    }

    ret &= xmlValidateOneElement(ctxt, doc, elem);
    if (elem->type == XML_ELEMENT_NODE) {
	attr = elem->properties;
	while (attr != NULL) {
	    value = xmlNodeListGetString(doc, attr->children, 0);
	    ret &= xmlValidateOneAttribute(ctxt, doc, elem, attr, value);
	    if (value != NULL)
		xmlFree((char *)value);
	    attr= attr->next;
	}
	ns = elem->nsDef;
	while (ns != NULL) {
	    if (elem->ns == NULL)
		ret &= xmlValidateOneNamespace(ctxt, doc, elem, NULL,
					       ns, ns->href);
	    else
		ret &= xmlValidateOneNamespace(ctxt, doc, elem,
		                               elem->ns->prefix, ns, ns->href);
	    ns = ns->next;
	}
    }
    child = elem->children;
    while (child != NULL) {
        ret &= xmlValidateElement(ctxt, doc, child);
        child = child->next;
    }

    return(ret);
}


xmlFreeNotation(xmlNotationPtr nota) {
    if (nota == NULL) return;
    if (nota->name != NULL)
	xmlFree((xmlChar *) nota->name);
    if (nota->PublicID != NULL)
	xmlFree((xmlChar *) nota->PublicID);
    if (nota->SystemID != NULL)
	xmlFree((xmlChar *) nota->SystemID);
    xmlFree(nota);
}


xmlFreeRefTable(xmlRefTablePtr table) {
    xmlHashFree(table, (xmlHashDeallocator) xmlFreeRefList);
}


xmlCopyNotation(xmlNotationPtr nota) {
    xmlNotationPtr cur;

    cur = (xmlNotationPtr) xmlMalloc(sizeof(xmlNotation));
    if (cur == NULL) {
	xmlVErrMemory(NULL, "malloc failed");
	return(NULL);
    }
    if (nota->name != NULL)
	cur->name = xmlStrdup(nota->name);
    else
	cur->name = NULL;
    if (nota->PublicID != NULL)
	cur->PublicID = xmlStrdup(nota->PublicID);
    else
	cur->PublicID = NULL;
    if (nota->SystemID != NULL)
	cur->SystemID = xmlStrdup(nota->SystemID);
    else
	cur->SystemID = NULL;
    return(cur);
}


vstateVPop(xmlValidCtxtPtr ctxt) {
    xmlElementPtr elemDecl;

    if (ctxt->vstateNr < 1) return(-1);
    ctxt->vstateNr--;
    elemDecl = ctxt->vstateTab[ctxt->vstateNr].elemDecl;
    ctxt->vstateTab[ctxt->vstateNr].elemDecl = NULL;
    ctxt->vstateTab[ctxt->vstateNr].node = NULL;
    if ((elemDecl != NULL) && (elemDecl->etype == XML_ELEMENT_TYPE_ELEMENT)) {
	xmlRegFreeExecCtxt(ctxt->vstateTab[ctxt->vstateNr].exec);
    }
    ctxt->vstateTab[ctxt->vstateNr].exec = NULL;
    if (ctxt->vstateNr >= 1)
	ctxt->vstate = &ctxt->vstateTab[ctxt->vstateNr - 1];
    else
	ctxt->vstate = NULL;
    return(ctxt->vstateNr);
}


xmlNewDocElementContent(xmlDocPtr doc, const xmlChar *name,
                        xmlElementContentType type) {
    xmlElementContentPtr ret;
    xmlDictPtr dict = NULL;

    if (doc != NULL)
        dict = doc->dict;

    switch(type) {
	case XML_ELEMENT_CONTENT_ELEMENT:
	    if (name == NULL) {
	        xmlErrValid(NULL, XML_ERR_INTERNAL_ERROR,
			"xmlNewElementContent : name == NULL !\n",
			NULL);
	    }
	    break;
        case XML_ELEMENT_CONTENT_PCDATA:
	case XML_ELEMENT_CONTENT_SEQ:
	case XML_ELEMENT_CONTENT_OR:
	    if (name != NULL) {
	        xmlErrValid(NULL, XML_ERR_INTERNAL_ERROR,
			"xmlNewElementContent : name != NULL !\n",
			NULL);
	    }
	    break;
	default:
	    xmlErrValid(NULL, XML_ERR_INTERNAL_ERROR,
		    "Internal: ELEMENT content corrupted invalid type\n",
		    NULL);
	    return(NULL);
    }
    ret = (xmlElementContentPtr) xmlMalloc(sizeof(xmlElementContent));
    if (ret == NULL) {
	xmlVErrMemory(NULL, "malloc failed");
	return(NULL);
    }
    memset(ret, 0, sizeof(xmlElementContent));
    ret->type = type;
    ret->ocur = XML_ELEMENT_CONTENT_ONCE;
    if (name != NULL) {
        int l;
	const xmlChar *tmp;

	tmp = xmlSplitQName3(name, &l);
	if (tmp == NULL) {
	    if (dict == NULL)
		ret->name = xmlStrdup(name);
	    else
	        ret->name = xmlDictLookup(dict, name, -1);
	} else {
	    if (dict == NULL) {
		ret->prefix = xmlStrndup(name, l);
		ret->name = xmlStrdup(tmp);
	    } else {
	        ret->prefix = xmlDictLookup(dict, name, l);
		ret->name = xmlDictLookup(dict, tmp, -1);
	    }
	}
    }
    return(ret);
}


int virtio_gpu_create_mapping_iov(struct virtio_gpu_resource_attach_backing *ab,
                                  struct virtio_gpu_ctrl_command *cmd,
                                  uint64_t **addr, struct iovec **iov)
{
    struct virtio_gpu_mem_entry *ents;
    size_t esize, s;
    int i;

    if (ab->nr_entries > 16384) {
        qemu_log_mask(LOG_GUEST_ERROR,
                      "%s: nr_entries is too big (%d > 16384)\n",
                      __func__, ab->nr_entries);
        return -1;
    }

    esize = sizeof(*ents) * ab->nr_entries;
    ents = g_malloc(esize);
    s = iov_to_buf(cmd->elem.out_sg, cmd->elem.out_num,
                   sizeof(*ab), ents, esize);
    if (s != esize) {
        qemu_log_mask(LOG_GUEST_ERROR,
                      "%s: command data size incorrect %zu vs %zu\n",
                      __func__, s, esize);
        g_free(ents);
        return -1;
    }

    *iov = g_malloc0(sizeof(struct iovec) * ab->nr_entries);
    if (addr) {
        *addr = g_malloc0(sizeof(uint64_t) * ab->nr_entries);
    }
    for (i = 0; i < ab->nr_entries; i++) {
        hwaddr len = ents[i].length;
        (*iov)[i].iov_len = ents[i].length;
        (*iov)[i].iov_base = cpu_physical_memory_map(ents[i].addr, &len, 1);
        if (addr) {
            (*addr)[i] = ents[i].addr;
        }
        if (!(*iov)[i].iov_base || len != ents[i].length) {
            qemu_log_mask(LOG_GUEST_ERROR, "%s: failed to map MMIO memory for"
                          " resource %d element %d\n",
                          __func__, ab->resource_id, i);
            virtio_gpu_cleanup_mapping_iov(*iov, i);
            g_free(ents);
            *iov = NULL;
            if (addr) {
                g_free(*addr);
                *addr = NULL;
            }
            return -1;
        }
    }
    g_free(ents);
    return 0;
}


static void update_cursor_data_virgl(VirtIOGPU *g,
                                     struct virtio_gpu_scanout *s,
                                     uint32_t resource_id)
{
    uint32_t width, height;
    uint32_t pixels, *data;

    data = virgl_renderer_get_cursor_data(resource_id, &width, &height);
    if (!data) {
        return;
    }

    if (width != s->current_cursor->width ||
        height != s->current_cursor->height) {
        free(data);
        return;
    }

    pixels = s->current_cursor->width * s->current_cursor->height;
    memcpy(s->current_cursor->data, data, pixels * sizeof(uint32_t));
    free(data);
}


e1000e_set_rx_control(E1000ECore *core, int index, uint32_t val)
{
    core->mac[RCTL] = val;
    trace_e1000e_rx_set_rctl(core->mac[RCTL]);

    if (val & E1000_RCTL_EN) {
        e1000e_parse_rxbufsize(core);
        e1000e_calc_rxdesclen(core);
        core->rxbuf_min_shift = ((val / E1000_RCTL_RDMTS_QUAT) & 3) + 1 +
                                E1000_RING_DESC_LEN_SHIFT;

        e1000e_start_recv(core);
    }
}


static uint32_t
e1000e_mac_read_clr4(E1000ECore *core, int index)
{
    uint32_t ret = core->mac[index];

    core->mac[index] = 0;
    return ret;


e1000e_tx_wb_interrupt_cause(E1000ECore *core, int queue_idx)
{
    if (!msix_enabled(core->owner)) {
        return E1000_ICR_TXDW;
    }

    return (queue_idx == 0) ? E1000_ICR_TXQ0 : E1000_ICR_TXQ1;
}


e1000e_intrmgr_resume(E1000ECore *core)
{
    int i;

    e1000e_intmgr_timer_resume(&core->radv);
    e1000e_intmgr_timer_resume(&core->rdtr);
    e1000e_intmgr_timer_resume(&core->raid);
    e1000e_intmgr_timer_resume(&core->tidv);
    e1000e_intmgr_timer_resume(&core->tadv);

    e1000e_intmgr_timer_resume(&core->itr);

    for (i = 0; i < E1000E_MSIX_VEC_NUM; i++) {
        e1000e_intmgr_timer_resume(&core->eitr[i]);
    }
}


e1000e_intrmgr_initialize_all_timers(E1000ECore *core, bool create)
{
    int i;

    core->radv.delay_reg = RADV;
    core->rdtr.delay_reg = RDTR;
    core->raid.delay_reg = RAID;
    core->tadv.delay_reg = TADV;
    core->tidv.delay_reg = TIDV;

    core->radv.delay_resolution_ns = E1000_INTR_DELAY_NS_RES;
    core->rdtr.delay_resolution_ns = E1000_INTR_DELAY_NS_RES;
    core->raid.delay_resolution_ns = E1000_INTR_DELAY_NS_RES;
    core->tadv.delay_resolution_ns = E1000_INTR_DELAY_NS_RES;
    core->tidv.delay_resolution_ns = E1000_INTR_DELAY_NS_RES;

    core->radv.core = core;
    core->rdtr.core = core;
    core->raid.core = core;
    core->tadv.core = core;
    core->tidv.core = core;

    core->itr.core = core;
    core->itr.delay_reg = ITR;
    core->itr.delay_resolution_ns = E1000_INTR_THROTTLING_NS_RES;

    for (i = 0; i < E1000E_MSIX_VEC_NUM; i++) {
        core->eitr[i].core = core;
        core->eitr[i].delay_reg = EITR + i;
        core->eitr[i].delay_resolution_ns = E1000_INTR_THROTTLING_NS_RES;
    }

    if (!create) {
        return;
    }

    core->radv.timer =
        timer_new_ns(QEMU_CLOCK_VIRTUAL, e1000e_intrmgr_on_timer, &core->radv);
    core->rdtr.timer =
        timer_new_ns(QEMU_CLOCK_VIRTUAL, e1000e_intrmgr_on_timer, &core->rdtr);
    core->raid.timer =
        timer_new_ns(QEMU_CLOCK_VIRTUAL, e1000e_intrmgr_on_timer, &core->raid);

    core->tadv.timer =
        timer_new_ns(QEMU_CLOCK_VIRTUAL, e1000e_intrmgr_on_timer, &core->tadv);
    core->tidv.timer =
        timer_new_ns(QEMU_CLOCK_VIRTUAL, e1000e_intrmgr_on_timer, &core->tidv);

    core->itr.timer = timer_new_ns(QEMU_CLOCK_VIRTUAL,
                                   e1000e_intrmgr_on_throttling_timer,
                                   &core->itr);

    for (i = 0; i < E1000E_MSIX_VEC_NUM; i++) {
        core->eitr[i].timer =
            timer_new_ns(QEMU_CLOCK_VIRTUAL,
                         e1000e_intrmgr_on_msix_throttling_timer,
                         &core->eitr[i]);
    }
}


static uint32_t
e1000e_mac_eitr_read(E1000ECore *core, int index)
{
    return core->eitr_guest_value[index - EITR];


e1000e_rx_fix_l4_csum(E1000ECore *core, struct NetRxPkt *pkt)
{
    if (net_rx_pkt_has_virt_hdr(pkt)) {
        struct virtio_net_hdr *vhdr = net_rx_pkt_get_vhdr(pkt);

        if (vhdr->flags & VIRTIO_NET_HDR_F_NEEDS_CSUM) {
            net_rx_pkt_fix_l4_csum(pkt);
        }
    }
}


static void
e1000e_mac_setmacaddr(E1000ECore *core, int index, uint32_t val)
{
    uint32_t macaddr[2];

    core->mac[index] = val;

    macaddr[0] = cpu_to_le32(core->mac[RA]);
    macaddr[1] = cpu_to_le32(core->mac[RA + 1]);
    qemu_format_nic_info_str(qemu_get_queue(core->owner_nic),
        (uint8_t *) macaddr);

    trace_e1000e_mac_set_sw(MAC_ARG(macaddr));


e1000e_set_fcrtl(E1000ECore *core, int index, uint32_t val)
{
    core->mac[FCRTL] = val & 0x8000FFF8;
}


e1000e_set_phy_ctrl(E1000ECore *core, int index, uint16_t val)
{
    /* bits 0-5 reserved; MII_CR_[RESTART_AUTO_NEG,RESET] are self clearing */
    core->phy[0][PHY_CTRL] = val & ~(0x3f |
                                     MII_CR_RESET |
                                     MII_CR_RESTART_AUTO_NEG);

    if ((val & MII_CR_RESTART_AUTO_NEG) &&
        e1000e_have_autoneg(core)) {
        e1000x_restart_autoneg(core->mac, core->phy[0], core->autoneg_timer);
    }
}


e1000e_eitr_should_postpone(E1000ECore *core, int idx)
{
    return e1000e_postpone_interrupt(&core->eitr_intr_pending[idx],
                                     &core->eitr[idx]);
}


void e1000e_core_pre_save(E1000ECore *core)
{
    int i;
    NetClientState *nc = qemu_get_queue(core->owner_nic);

    /*
    * If link is down and auto-negotiation is supported and ongoing,
    * complete auto-negotiation immediately. This allows us to look
    * at MII_SR_AUTONEG_COMPLETE to infer link status on load.
    */
    if (nc->link_down && e1000e_have_autoneg(core)) {
        core->phy[0][PHY_STATUS] |= MII_SR_AUTONEG_COMPLETE;
        e1000e_update_flowctl_status(core);
    }

    for (i = 0; i < ARRAY_SIZE(core->tx); i++) {
        if (net_tx_pkt_has_fragments(core->tx[i].tx_pkt)) {
            core->tx[i].skip_cp = true;
        }
    }


e1000e_set_12bit(E1000ECore *core, int index, uint32_t val)
{
    core->mac[index] = val & 0xfff;
}


e1000e_set_phy_page(E1000ECore *core, int index, uint16_t val)
{
    core->phy[0][PHY_PAGE] = val & PHY_PAGE_RW_MASK;
}


e1000e_write_to_rx_buffers(E1000ECore *core,
                           hwaddr (*ba)[MAX_PS_BUFFERS],
                           e1000e_ba_state *bastate,
                           const char *data,
                           dma_addr_t data_len)
{
    while (data_len > 0) {
        uint32_t cur_buf_len = core->rxbuf_sizes[bastate->cur_idx];
        uint32_t cur_buf_bytes_left = cur_buf_len -
                                      bastate->written[bastate->cur_idx];
        uint32_t bytes_to_write = MIN(data_len, cur_buf_bytes_left);

        trace_e1000e_rx_desc_buff_write(bastate->cur_idx,
                                        (*ba)[bastate->cur_idx],
                                        bastate->written[bastate->cur_idx],
                                        data,
                                        bytes_to_write);

        pci_dma_write(core->owner,
            (*ba)[bastate->cur_idx] + bastate->written[bastate->cur_idx],
            data, bytes_to_write);

        bastate->written[bastate->cur_idx] += bytes_to_write;
        data += bytes_to_write;
        data_len -= bytes_to_write;

        if (bastate->written[bastate->cur_idx] == cur_buf_len) {
            bastate->cur_idx++;
        }

        assert(bastate->cur_idx < MAX_PS_BUFFERS);
    }
}


void
e1000e_core_reset(E1000ECore *core)
{
    int i;

    timer_del(core->autoneg_timer);

    e1000e_intrmgr_reset(core);

    memset(core->phy, 0, sizeof core->phy);
    memmove(core->phy, e1000e_phy_reg_init, sizeof e1000e_phy_reg_init);
    memset(core->mac, 0, sizeof core->mac);
    memmove(core->mac, e1000e_mac_reg_init, sizeof e1000e_mac_reg_init);

    core->rxbuf_min_shift = 1 + E1000_RING_DESC_LEN_SHIFT;

    if (qemu_get_queue(core->owner_nic)->link_down) {
        e1000e_link_down(core);
    }

    e1000x_reset_mac_addr(core->owner_nic, core->mac, core->permanent_mac);

    for (i = 0; i < ARRAY_SIZE(core->tx); i++) {
        net_tx_pkt_reset(core->tx[i].tx_pkt);
        memset(&core->tx[i].props, 0, sizeof(core->tx[i].props));
        core->tx[i].skip_cp = false;
    }


e1000e_intrmgr_on_msix_throttling_timer(void *opaque)
{
    E1000IntrDelayTimer *timer = opaque;
    int idx = timer - &timer->core->eitr[0];

    assert(msix_enabled(timer->core->owner));

    timer->running = false;

    if (!timer->core->eitr_intr_pending[idx]) {
        trace_e1000e_irq_throttling_no_pending_vec(idx);
        return;
    }

    trace_e1000e_irq_msix_notify_postponed_vec(idx);
    msix_notify(timer->core->owner, idx);
}


e1000e_mac_ics_read(E1000ECore *core, int index)
{
    trace_e1000e_irq_read_ics(core->mac[ICS]);
    return core->mac[ICS];
}