<commit_before>#include "StdAfx.h"
#include "NefDecoder.h"
#include "ByteStreamSwap.h"
#include "BitPumpMSB32.h"
/*
    RawSpeed - RAW file decoder.

    Copyright (C) 2009 Klaus Post

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Lesser General Public
    License as published by the Free Software Foundation; either
    version 2 of the License, or (at your option) any later version.

    This library is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public
    License along with this library; if not, write to the Free Software
    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA

    http://www.klauspost.com
*/

namespace RawSpeed {

NefDecoder::NefDecoder(TiffIFD *rootIFD, FileMap* file) :
    RawDecoder(file), mRootIFD(rootIFD) {
  decoderVersion = 3;
}

NefDecoder::~NefDecoder(void) {
  if (mRootIFD)
    delete mRootIFD;
  mRootIFD = NULL;
}

RawImage NefDecoder::decodeRawInternal() {
  vector<TiffIFD*> data = mRootIFD->getIFDsWithTag(CFAPATTERN);

  if (data.empty())
    ThrowRDE("NEF Decoder: No image data found");

  TiffIFD* raw = data[0];
  int compression = raw->getEntry(COMPRESSION)->getInt();

  data = mRootIFD->getIFDsWithTag(MODEL);

  if (data.empty())
    ThrowRDE("NEF Decoder: No model data found");

  TiffEntry *offsets = raw->getEntry(STRIPOFFSETS);
  TiffEntry *counts = raw->getEntry(STRIPBYTECOUNTS);

  if (!data[0]->getEntry(MODEL)->getString().compare("NIKON D100 ")) {  /**Sigh**/
    if (!mFile->isValid(offsets->getInt()))
      ThrowRDE("NEF Decoder: Image data outside of file.");
    if (!D100IsCompressed(offsets->getInt())) {
      DecodeD100Uncompressed();
      return mRaw;
    }
  }

  if (compression == 1) {
    DecodeUncompressed();
    return mRaw;
  }

  if (offsets->count != 1) {
    ThrowRDE("NEF Decoder: Multiple Strips found: %u", offsets->count);
  }
  if (counts->count != offsets->count) {
    ThrowRDE("NEF Decoder: Byte count number does not match strip size: count:%u, strips:%u ", counts->count, offsets->count);
  }
  if (!mFile->isValid(offsets->getInt() + counts->getInt()))
    ThrowRDE("NEF Decoder: Invalid strip byte count. File probably truncated.");

  if (34713 != compression)
    ThrowRDE("NEF Decoder: Unsupported compression");

  uint32 width = raw->getEntry(IMAGEWIDTH)->getInt();
  uint32 height = raw->getEntry(IMAGELENGTH)->getInt();
  uint32 bitPerPixel = raw->getEntry(BITSPERSAMPLE)->getInt();

  mRaw->dim = iPoint2D(width, height);
  mRaw->createData();

  data = mRootIFD->getIFDsWithTag(MAKERNOTE);
  if (data.empty())
    ThrowRDE("NEF Decoder: No EXIF data found");

  TiffIFD* exif = data[0];
  TiffEntry *makernoteEntry = exif->getEntry(MAKERNOTE);
  const uchar8* makernote = makernoteEntry->getData();
  FileMap makermap((uchar8*)&makernote[10], mFile->getSize() - makernoteEntry->getDataOffset() - 10);
  TiffParser makertiff(&makermap);
  makertiff.parseData();

  data = makertiff.RootIFD()->getIFDsWithTag((TiffTag)0x8c);

  if (data.empty())
    ThrowRDE("NEF Decoder: Decompression info tag not found");

  TiffEntry *meta;
  try {
    meta = data[0]->getEntry((TiffTag)0x96);
  } catch (TiffParserException) {
    meta = data[0]->getEntry((TiffTag)0x8c);  // Fall back
  }

  try {
    NikonDecompressor decompressor(mFile, mRaw);

    // Nikon is JPEG (Big Endian) byte order
    ByteStream* metastream;
    if (getHostEndianness() == big)
      metastream = new ByteStream(meta->getData(), meta->count);
    else
      metastream = new ByteStreamSwap(meta->getData(), meta->count);

    decompressor.DecompressNikon(metastream, width, height, bitPerPixel, offsets->getInt(), counts->getInt());

    delete metastream;
  } catch (IOException &e) {
    mRaw->setError(e.what());
    // Let's ignore it, it may have delivered somewhat useful data.
  }

  return mRaw;
}

/*
Figure out if a NEF file is compressed.  These fancy heuristics
are only needed for the D100, thanks to a bug in some cameras
that tags all images as "compressed".
*/
bool NefDecoder::D100IsCompressed(uint32 offset) {
  const uchar8 *test = mFile->getData(offset);
  int i;

  for (i = 15; i < 256; i += 16)
    if (test[i]) return true;
  return false;
}

TiffIFD* NefDecoder::FindBestImage(vector<TiffIFD*>* data) {
  int largest_width = 0;
  TiffIFD* best_ifd = NULL;
  for (int i = 0; i < (int)data->size(); i++) {
    TiffIFD* raw = (*data)[i];
    int width = raw->getEntry(IMAGEWIDTH)->getInt();
    if (width > largest_width)
      best_ifd = raw;
  }
  if (NULL == best_ifd)
    ThrowRDE("NEF Decoder: Unable to locate image");
  return best_ifd;
}

void NefDecoder::DecodeUncompressed() {
  vector<TiffIFD*> data = mRootIFD->getIFDsWithTag(CFAPATTERN);
  TiffIFD* raw = FindBestImage(&data);
  uint32 nslices = raw->getEntry(STRIPOFFSETS)->count;
  const uint32 *offsets = raw->getEntry(STRIPOFFSETS)->getIntArray();
  const uint32 *counts = raw->getEntry(STRIPBYTECOUNTS)->getIntArray();
  uint32 yPerSlice = raw->getEntry(ROWSPERSTRIP)->getInt();
  uint32 width = raw->getEntry(IMAGEWIDTH)->getInt();
  uint32 height = raw->getEntry(IMAGELENGTH)->getInt();
  uint32 bitPerPixel = raw->getEntry(BITSPERSAMPLE)->getInt();

  vector<NefSlice> slices;
  uint32 offY = 0;

  for (uint32 s = 0; s < nslices; s++) {
    NefSlice slice;
    slice.offset = offsets[s];
    slice.count = counts[s];
    if (offY + yPerSlice > height)
      slice.h = height - offY;
    else
      slice.h = yPerSlice;

    offY += yPerSlice;

    if (mFile->isValid(slice.offset + slice.count)) // Only decode if size is valid
      slices.push_back(slice);
  }

  if (0 == slices.size())
    ThrowRDE("NEF Decoder: No valid slices found. File probably truncated.");

  mRaw->dim = iPoint2D(width, offY);
  mRaw->createData();
  if (bitPerPixel == 14 && width*slices[0].h*2 == slices[0].count)
    bitPerPixel = 16; // D3

  offY = 0;
  for (uint32 i = 0; i < slices.size(); i++) {
    NefSlice slice = slices[i];
    ByteStream in(mFile->getData(slice.offset), slice.count);
    iPoint2D size(width, slice.h);
    iPoint2D pos(0, offY);
    try {
      if (hints.find(string("coolpixmangled")) != hints.end())
        readCoolpixMangledRaw(in, size, pos, width*bitPerPixel / 8);
      else if (hints.find(string("coolpixsplit")) != hints.end())
        readCoolpixSplitRaw(in, size, pos, width*bitPerPixel / 8);
      else
        readUncompressedRaw(in, size, pos, width*bitPerPixel / 8, bitPerPixel, true);
    } catch (RawDecoderException e) {
      if (i>0)
        mRaw->setError(e.what());
      else
        throw;
    } catch (IOException e) {
      if (i>0)
        mRaw->setError(e.what());
      else
        ThrowRDE("NEF decoder: IO error occurred in first slice, unable to decode more. Error is: %s", e.what());
    }
    offY += slice.h;
  }
}

void NefDecoder::readCoolpixMangledRaw(ByteStream &input, iPoint2D& size, iPoint2D& offset, int inputPitch) {
  uchar8* data = mRaw->getData();
  uint32 outPitch = mRaw->pitch;
  uint32 w = size.x;
  uint32 h = size.y;
  uint32 cpp = mRaw->getCpp();
  if (input.getRemainSize() < (inputPitch*h)) {
    if ((int)input.getRemainSize() > inputPitch)
      h = input.getRemainSize() / inputPitch - 1;
    else
      ThrowIOE("readUncompressedRaw: Not enough data to decode a single line. Image file truncated.");
  }

  if (offset.y > mRaw->dim.y)
    ThrowRDE("readUncompressedRaw: Invalid y offset");
  if (offset.x + size.x > mRaw->dim.x)
    ThrowRDE("readUncompressedRaw: Invalid x offset");

  uint32 y = offset.y;
  h = MIN(h + (uint32)offset.y, (uint32)mRaw->dim.y);
  w *= cpp;
  BitPumpMSB32 *in = new BitPumpMSB32(&input);
  for (; y < h; y++) {
    ushort16* dest = (ushort16*) & data[offset.x*sizeof(ushort16)*cpp+y*outPitch];
    for (uint32 x = 0 ; x < w; x++) {
      dest[x] =  in->getBits(12);
    }
  }
}

void NefDecoder::readCoolpixSplitRaw(ByteStream &input, iPoint2D& size, iPoint2D& offset, int inputPitch) {
  uchar8* data = mRaw->getData();
  uint32 outPitch = mRaw->pitch;
  uint32 w = size.x;
  uint32 h = size.y;
  uint32 cpp = mRaw->getCpp();
  if (input.getRemainSize() < (inputPitch*h)) {
    if ((int)input.getRemainSize() > inputPitch)
      h = input.getRemainSize() / inputPitch - 1;
    else
      ThrowIOE("readUncompressedRaw: Not enough data to decode a single line. Image file truncated.");
  }

  if (offset.y > mRaw->dim.y)
    ThrowRDE("readCoolpixSplitRaw: Invalid y offset");
  if (offset.x + size.x > mRaw->dim.x)
    ThrowRDE("readCoolpixSplitRaw: Invalid x offset");

  uint32 y = offset.y;
  h = MIN(h + (uint32)offset.y, (uint32)mRaw->dim.y);
  w *= cpp;
  h /= 2;
  BitPumpMSB *in = new BitPumpMSB(&input);
  for (; y < h; y++) {
    ushort16* dest = (ushort16*) & data[offset.x*sizeof(ushort16)*cpp+y*2*outPitch];
    for (uint32 x = 0 ; x < w; x++) {
      dest[x] =  in->getBits(12);
    }
  }
  for (y = offset.y; y < h; y++) {
    ushort16* dest = (ushort16*) & data[offset.x*sizeof(ushort16)*cpp+(y*2+1)*outPitch];
    for (uint32 x = 0 ; x < w; x++) {
      dest[x] =  in->getBits(12);
    }
  }
}

void NefDecoder::DecodeD100Uncompressed() {

  ThrowRDE("NEF DEcoder: D100 uncompressed not supported");
  /*
    TiffIFD* raw = mRootIFD->getIFDsWithTag(CFAPATTERN)[0];

    uint32 nslices = raw->getEntry(STRIPOFFSETS)->count;
    uint32 offset = raw->getEntry(STRIPOFFSETS)->getInt();
    uint32 count = raw->getEntry(STRIPBYTECOUNTS)->getInt();
    if (!mFile->isValid(offset+count))
      ThrowRDE("DecodeD100Uncompressed: Truncated file");

    const uchar8 *in =  mFile->getData(offset);

    uint32 w = raw->getEntry(IMAGEWIDTH)->getInt();
    uint32 h = raw->getEntry(IMAGELENGTH)->getInt();

    mRaw->dim = iPoint2D(w, h);
    mRaw->bpp = 2;
    mRaw->createData();

    uchar8* data = mRaw->getData();
    uint32 outPitch = mRaw->pitch;

    BitPumpMSB bits(mFile->getData(offset),count);
    for (uint32 y = 0; y < h; y++) {
      ushort16* dest = (ushort16*)&data[y*outPitch];
      for(uint32 x =0 ; x < w; x++) {
        uint32 b = bits.getBits(12);
        dest[x] = b;
        if ((x % 10) == 9)
          bits.skipBits(8);
      }
    }*/
}

void NefDecoder::checkSupportInternal(CameraMetaData *meta) {
  vector<TiffIFD*> data = mRootIFD->getIFDsWithTag(MODEL);
  if (data.empty())
    ThrowRDE("NEF Support check: Model name found");
  string make = data[0]->getEntry(MAKE)->getString();
  string model = data[0]->getEntry(MODEL)->getString();
  this->checkCameraSupported(meta, make, model, "");
}

void NefDecoder::decodeMetaDataInternal(CameraMetaData *meta) {
  int iso = 0;
  mRaw->cfa.setCFA(CFA_RED, CFA_GREEN, CFA_GREEN2, CFA_BLUE);

  vector<TiffIFD*> data = mRootIFD->getIFDsWithTag(MODEL);

  if (data.empty())
    ThrowRDE("NEF Meta Decoder: Model name found");
  if (!data[0]->hasEntry(MAKE))
    ThrowRDE("NEF Support: Make name not found");

  int white = mRaw->whitePoint;
  int black = mRaw->blackLevel;

  string make = data[0]->getEntry(MAKE)->getString();
  string model = data[0]->getEntry(MODEL)->getString();

  if (mRootIFD->hasEntryRecursive(ISOSPEEDRATINGS))
    iso = mRootIFD->getEntryRecursive(ISOSPEEDRATINGS)->getInt();

  setMetaData(meta, make, model, "", iso);

  if (white != 65536)
    mRaw->whitePoint = white;
  if (black >= 0)
    mRaw->blackLevel = black;
}

} // namespace RawSpeed
<commit_msg>Don't assume endianness of metadata.<commit_after>#include "StdAfx.h"
#include "NefDecoder.h"
#include "ByteStreamSwap.h"
#include "BitPumpMSB32.h"
/*
    RawSpeed - RAW file decoder.

    Copyright (C) 2009 Klaus Post

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Lesser General Public
    License as published by the Free Software Foundation; either
    version 2 of the License, or (at your option) any later version.

    This library is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public
    License along with this library; if not, write to the Free Software
    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA

    http://www.klauspost.com
*/

namespace RawSpeed {

NefDecoder::NefDecoder(TiffIFD *rootIFD, FileMap* file) :
    RawDecoder(file), mRootIFD(rootIFD) {
  decoderVersion = 3;
}

NefDecoder::~NefDecoder(void) {
  if (mRootIFD)
    delete mRootIFD;
  mRootIFD = NULL;
}

RawImage NefDecoder::decodeRawInternal() {
  vector<TiffIFD*> data = mRootIFD->getIFDsWithTag(CFAPATTERN);

  if (data.empty())
    ThrowRDE("NEF Decoder: No image data found");

  TiffIFD* raw = data[0];
  int compression = raw->getEntry(COMPRESSION)->getInt();

  data = mRootIFD->getIFDsWithTag(MODEL);

  if (data.empty())
    ThrowRDE("NEF Decoder: No model data found");

  TiffEntry *offsets = raw->getEntry(STRIPOFFSETS);
  TiffEntry *counts = raw->getEntry(STRIPBYTECOUNTS);

  if (!data[0]->getEntry(MODEL)->getString().compare("NIKON D100 ")) {  /**Sigh**/
    if (!mFile->isValid(offsets->getInt()))
      ThrowRDE("NEF Decoder: Image data outside of file.");
    if (!D100IsCompressed(offsets->getInt())) {
      DecodeD100Uncompressed();
      return mRaw;
    }
  }

  if (compression == 1) {
    DecodeUncompressed();
    return mRaw;
  }

  if (offsets->count != 1) {
    ThrowRDE("NEF Decoder: Multiple Strips found: %u", offsets->count);
  }
  if (counts->count != offsets->count) {
    ThrowRDE("NEF Decoder: Byte count number does not match strip size: count:%u, strips:%u ", counts->count, offsets->count);
  }
  if (!mFile->isValid(offsets->getInt() + counts->getInt()))
    ThrowRDE("NEF Decoder: Invalid strip byte count. File probably truncated.");

  if (34713 != compression)
    ThrowRDE("NEF Decoder: Unsupported compression");

  uint32 width = raw->getEntry(IMAGEWIDTH)->getInt();
  uint32 height = raw->getEntry(IMAGELENGTH)->getInt();
  uint32 bitPerPixel = raw->getEntry(BITSPERSAMPLE)->getInt();

  mRaw->dim = iPoint2D(width, height);
  mRaw->createData();

  data = mRootIFD->getIFDsWithTag(MAKERNOTE);
  if (data.empty())
    ThrowRDE("NEF Decoder: No EXIF data found");

  TiffIFD* exif = data[0];
  TiffEntry *makernoteEntry = exif->getEntry(MAKERNOTE);
  const uchar8* makernote = makernoteEntry->getData();
  FileMap makermap((uchar8*)&makernote[10], mFile->getSize() - makernoteEntry->getDataOffset() - 10);
  TiffParser makertiff(&makermap);
  makertiff.parseData();

  data = makertiff.RootIFD()->getIFDsWithTag((TiffTag)0x8c);

  if (data.empty())
    ThrowRDE("NEF Decoder: Decompression info tag not found");

  TiffEntry *meta;
  try {
    meta = data[0]->getEntry((TiffTag)0x96);
  } catch (TiffParserException) {
    meta = data[0]->getEntry((TiffTag)0x8c);  // Fall back
  }

  try {
    NikonDecompressor decompressor(mFile, mRaw);

    ByteStream* metastream;
    if (getHostEndianness() == data[0]->endian)
      metastream = new ByteStream(meta->getData(), meta->count);
    else
      metastream = new ByteStreamSwap(meta->getData(), meta->count);

    decompressor.DecompressNikon(metastream, width, height, bitPerPixel, offsets->getInt(), counts->getInt());

    delete metastream;
  } catch (IOException &e) {
    mRaw->setError(e.what());
    // Let's ignore it, it may have delivered somewhat useful data.
  }

  return mRaw;
}

/*
Figure out if a NEF file is compressed.  These fancy heuristics
are only needed for the D100, thanks to a bug in some cameras
that tags all images as "compressed".
*/
bool NefDecoder::D100IsCompressed(uint32 offset) {
  const uchar8 *test = mFile->getData(offset);
  int i;

  for (i = 15; i < 256; i += 16)
    if (test[i]) return true;
  return false;
}

TiffIFD* NefDecoder::FindBestImage(vector<TiffIFD*>* data) {
  int largest_width = 0;
  TiffIFD* best_ifd = NULL;
  for (int i = 0; i < (int)data->size(); i++) {
    TiffIFD* raw = (*data)[i];
    int width = raw->getEntry(IMAGEWIDTH)->getInt();
    if (width > largest_width)
      best_ifd = raw;
  }
  if (NULL == best_ifd)
    ThrowRDE("NEF Decoder: Unable to locate image");
  return best_ifd;
}

void NefDecoder::DecodeUncompressed() {
  vector<TiffIFD*> data = mRootIFD->getIFDsWithTag(CFAPATTERN);
  TiffIFD* raw = FindBestImage(&data);
  uint32 nslices = raw->getEntry(STRIPOFFSETS)->count;
  const uint32 *offsets = raw->getEntry(STRIPOFFSETS)->getIntArray();
  const uint32 *counts = raw->getEntry(STRIPBYTECOUNTS)->getIntArray();
  uint32 yPerSlice = raw->getEntry(ROWSPERSTRIP)->getInt();
  uint32 width = raw->getEntry(IMAGEWIDTH)->getInt();
  uint32 height = raw->getEntry(IMAGELENGTH)->getInt();
  uint32 bitPerPixel = raw->getEntry(BITSPERSAMPLE)->getInt();

  vector<NefSlice> slices;
  uint32 offY = 0;

  for (uint32 s = 0; s < nslices; s++) {
    NefSlice slice;
    slice.offset = offsets[s];
    slice.count = counts[s];
    if (offY + yPerSlice > height)
      slice.h = height - offY;
    else
      slice.h = yPerSlice;

    offY += yPerSlice;

    if (mFile->isValid(slice.offset + slice.count)) // Only decode if size is valid
      slices.push_back(slice);
  }

  if (0 == slices.size())
    ThrowRDE("NEF Decoder: No valid slices found. File probably truncated.");

  mRaw->dim = iPoint2D(width, offY);
  mRaw->createData();
  if (bitPerPixel == 14 && width*slices[0].h*2 == slices[0].count)
    bitPerPixel = 16; // D3

  offY = 0;
  for (uint32 i = 0; i < slices.size(); i++) {
    NefSlice slice = slices[i];
    ByteStream in(mFile->getData(slice.offset), slice.count);
    iPoint2D size(width, slice.h);
    iPoint2D pos(0, offY);
    try {
      if (hints.find(string("coolpixmangled")) != hints.end())
        readCoolpixMangledRaw(in, size, pos, width*bitPerPixel / 8);
      else if (hints.find(string("coolpixsplit")) != hints.end())
        readCoolpixSplitRaw(in, size, pos, width*bitPerPixel / 8);
      else
        readUncompressedRaw(in, size, pos, width*bitPerPixel / 8, bitPerPixel, true);
    } catch (RawDecoderException e) {
      if (i>0)
        mRaw->setError(e.what());
      else
        throw;
    } catch (IOException e) {
      if (i>0)
        mRaw->setError(e.what());
      else
        ThrowRDE("NEF decoder: IO error occurred in first slice, unable to decode more. Error is: %s", e.what());
    }
    offY += slice.h;
  }
}

void NefDecoder::readCoolpixMangledRaw(ByteStream &input, iPoint2D& size, iPoint2D& offset, int inputPitch) {
  uchar8* data = mRaw->getData();
  uint32 outPitch = mRaw->pitch;
  uint32 w = size.x;
  uint32 h = size.y;
  uint32 cpp = mRaw->getCpp();
  if (input.getRemainSize() < (inputPitch*h)) {
    if ((int)input.getRemainSize() > inputPitch)
      h = input.getRemainSize() / inputPitch - 1;
    else
      ThrowIOE("readUncompressedRaw: Not enough data to decode a single line. Image file truncated.");
  }

  if (offset.y > mRaw->dim.y)
    ThrowRDE("readUncompressedRaw: Invalid y offset");
  if (offset.x + size.x > mRaw->dim.x)
    ThrowRDE("readUncompressedRaw: Invalid x offset");

  uint32 y = offset.y;
  h = MIN(h + (uint32)offset.y, (uint32)mRaw->dim.y);
  w *= cpp;
  BitPumpMSB32 *in = new BitPumpMSB32(&input);
  for (; y < h; y++) {
    ushort16* dest = (ushort16*) & data[offset.x*sizeof(ushort16)*cpp+y*outPitch];
    for (uint32 x = 0 ; x < w; x++) {
      dest[x] =  in->getBits(12);
    }
  }
}

void NefDecoder::readCoolpixSplitRaw(ByteStream &input, iPoint2D& size, iPoint2D& offset, int inputPitch) {
  uchar8* data = mRaw->getData();
  uint32 outPitch = mRaw->pitch;
  uint32 w = size.x;
  uint32 h = size.y;
  uint32 cpp = mRaw->getCpp();
  if (input.getRemainSize() < (inputPitch*h)) {
    if ((int)input.getRemainSize() > inputPitch)
      h = input.getRemainSize() / inputPitch - 1;
    else
      ThrowIOE("readUncompressedRaw: Not enough data to decode a single line. Image file truncated.");
  }

  if (offset.y > mRaw->dim.y)
    ThrowRDE("readCoolpixSplitRaw: Invalid y offset");
  if (offset.x + size.x > mRaw->dim.x)
    ThrowRDE("readCoolpixSplitRaw: Invalid x offset");

  uint32 y = offset.y;
  h = MIN(h + (uint32)offset.y, (uint32)mRaw->dim.y);
  w *= cpp;
  h /= 2;
  BitPumpMSB *in = new BitPumpMSB(&input);
  for (; y < h; y++) {
    ushort16* dest = (ushort16*) & data[offset.x*sizeof(ushort16)*cpp+y*2*outPitch];
    for (uint32 x = 0 ; x < w; x++) {
      dest[x] =  in->getBits(12);
    }
  }
  for (y = offset.y; y < h; y++) {
    ushort16* dest = (ushort16*) & data[offset.x*sizeof(ushort16)*cpp+(y*2+1)*outPitch];
    for (uint32 x = 0 ; x < w; x++) {
      dest[x] =  in->getBits(12);
    }
  }
}

void NefDecoder::DecodeD100Uncompressed() {

  ThrowRDE("NEF DEcoder: D100 uncompressed not supported");
  /*
    TiffIFD* raw = mRootIFD->getIFDsWithTag(CFAPATTERN)[0];

    uint32 nslices = raw->getEntry(STRIPOFFSETS)->count;
    uint32 offset = raw->getEntry(STRIPOFFSETS)->getInt();
    uint32 count = raw->getEntry(STRIPBYTECOUNTS)->getInt();
    if (!mFile->isValid(offset+count))
      ThrowRDE("DecodeD100Uncompressed: Truncated file");

    const uchar8 *in =  mFile->getData(offset);

    uint32 w = raw->getEntry(IMAGEWIDTH)->getInt();
    uint32 h = raw->getEntry(IMAGELENGTH)->getInt();

    mRaw->dim = iPoint2D(w, h);
    mRaw->bpp = 2;
    mRaw->createData();

    uchar8* data = mRaw->getData();
    uint32 outPitch = mRaw->pitch;

    BitPumpMSB bits(mFile->getData(offset),count);
    for (uint32 y = 0; y < h; y++) {
      ushort16* dest = (ushort16*)&data[y*outPitch];
      for(uint32 x =0 ; x < w; x++) {
        uint32 b = bits.getBits(12);
        dest[x] = b;
        if ((x % 10) == 9)
          bits.skipBits(8);
      }
    }*/
}

void NefDecoder::checkSupportInternal(CameraMetaData *meta) {
  vector<TiffIFD*> data = mRootIFD->getIFDsWithTag(MODEL);
  if (data.empty())
    ThrowRDE("NEF Support check: Model name found");
  string make = data[0]->getEntry(MAKE)->getString();
  string model = data[0]->getEntry(MODEL)->getString();
  this->checkCameraSupported(meta, make, model, "");
}

void NefDecoder::decodeMetaDataInternal(CameraMetaData *meta) {
  int iso = 0;
  mRaw->cfa.setCFA(CFA_RED, CFA_GREEN, CFA_GREEN2, CFA_BLUE);

  vector<TiffIFD*> data = mRootIFD->getIFDsWithTag(MODEL);

  if (data.empty())
    ThrowRDE("NEF Meta Decoder: Model name found");
  if (!data[0]->hasEntry(MAKE))
    ThrowRDE("NEF Support: Make name not found");

  int white = mRaw->whitePoint;
  int black = mRaw->blackLevel;

  string make = data[0]->getEntry(MAKE)->getString();
  string model = data[0]->getEntry(MODEL)->getString();

  if (mRootIFD->hasEntryRecursive(ISOSPEEDRATINGS))
    iso = mRootIFD->getEntryRecursive(ISOSPEEDRATINGS)->getInt();

  setMetaData(meta, make, model, "", iso);

  if (white != 65536)
    mRaw->whitePoint = white;
  if (black >= 0)
    mRaw->blackLevel = black;
}

} // namespace RawSpeed
<|endoftext|>


<commit_before>#include <iostream>
#include <string>
#include <cstring>

#include "OpenCLHelper.h"
#include "stringhelper.h"
#include "Timer.h"
#include "test/TestArgsParser.h"
#include "test/SpeedTemplates.h"

using namespace std;

// for use in conjunction with http://www.cs.berkeley.edu/~volkov/volkov10-GTC.pdf
int main( int argc, char *argv[] ) {
    OpenCLHelper *cl = OpenCLHelper::createForFirstGpuOtherwiseCpu();

//    int its = 10000000;
    int numFloats = 50000000;
    int workgroupSize = 512;
    bool optimizerOn = true;
    int count = 1;
//    bool enableMad = true;
    int kernelVersion = 1;

    TestArgsParser args( argc, argv );
//    args._arg( "its", &its );
    args._arg( "n", &numFloats );
    args._arg( "workgroupsize", &workgroupSize );
    args._arg( "opt", &optimizerOn );
    args._arg( "count", &count );
    args._arg( "kernel", &kernelVersion );
//    args._arg( "mad", &enableMad );
    args._go();
    cout << "values:" << endl;
    args._printValues();

    count = OpenCLHelper::getNextPower2( count );
    int thiscount = count;
    int shift = 1;
    while( thiscount > 1 ) {
        thiscount >>= 1;
        shift++;
    }
    cout << "count: " << count << " shift: " << shift << endl;

//    cout << "its: " << its << " workgroupsize: " << workgroupSize << " optimizer: " << optimizerOn << endl;

    string kernelSource1 = R"DELIM(
        kernel void memcpy( global float const*src, global float *dest) {
            if( get_global_id(0) < N ) {
                float a = src[get_global_id(0)];
                dest[get_global_id(0)] = a;
            }
        }
    )DELIM";

    string kernelSource2 = R"DELIM(
        kernel void memcpy(global float const*src, global float *dest) {
            if( get_global_id(0) < N ) {
                dest[get_global_id(0)] = src[get_global_id(0)];
            }
        }
    )DELIM";

    string kernelSource3 = R"DELIM(
        kernel void memcpy(global float const*src, global float *dest) {
            dest[get_global_id(0)] = src[get_global_id(0)];
        }
    )DELIM";

    string kernelSource4 = R"DELIM(
        kernel void memcpy(global float const*src, global float *dest) {
            float a[COUNT];
            int offset = get_global_id(0) << SHIFT;
//            if( offset < N ) {
            if( get_global_id(0) < ( N >> SHIFT ) ) {
                #pragma unroll COUNT
                for( int i = 0; i < COUNT; i++ ) {
                    a[i] = src[ offset + i ];
                }
                #pragma unroll COUNT
                for( int i = 0; i < COUNT; i++ ) {
                    dest[ offset + i ] = a[i];
                }
            }
        }
    )DELIM";

    string kernelSource5 = R"DELIM(
        kernel void memcpy(global float const*src, global float *dest) {
            float a[COUNT];
            int offset = get_global_id(0) << SHIFT;
//            if( offset < N ) {
            if( get_global_id(0) < ( N >> SHIFT ) ) {
                {% for i in range( COUNT ) %}
                    a[{{i}}] = src[ offset + {{i}} ];
                {% endfor %}
                {% for i in range( COUNT ) %}
                    dest[ offset + {{i}} ] = a[{{i}}];
                {% endfor %}
            }
        }
    )DELIM";

    string kernelSource6 = R"DELIM(
        #define f4(var) ( (global float4*)(var) )

        kernel void memcpy(global float const*src, global float *dest) {
            float4 a[COUNT];
            int offset = get_global_id(0) << SHIFT;
            if( get_global_id(0) < ( N >> SHIFT ) ) {
                #pragma unroll COUNT
                for( int i = 0; i < COUNT; i++ ) {
                    a[i] = f4(src)[ offset + i ];
                }
                #pragma unroll COUNT
                for( int i = 0; i < COUNT; i++ ) {
                    f4(dest)[ offset + i ] = a[i];
                }
            }
        }
    )DELIM";

    string kernelSource7 = R"DELIM(
        #define f4(var) ( (global float4*)(var) )

        kernel void memcpy(global float const*src, global float *dest) {
            float4 a[COUNT];
            int offset = get_global_id(0) << SHIFT;
            if( get_global_id(0) < ( N >> SHIFT ) ) {
                {% for i in range( COUNT ) %}
                    a[{{i}}] = f4(src)[ offset + {{i}} ];
                {% endfor %}
                {% for i in range( COUNT ) %}
                    f4(dest)[ offset + {{i}} ] = a[{{i}}];
                {% endfor %}
            }
        }
    )DELIM";

    string options = "";
    if( !optimizerOn ) {
        options += " -cl-opt-disable";
    }
//    if( enableMad ) {
        options += " -cl-mad-enable";
//    }
    options += " -D COUNT=" + toString( count );
    options += " -D SHIFT=" + toString( shift );
//    if( kernelVersion == 1 && count == 4 ) {
//        options += " -D dOn";
//    }
    options += " -cl-single-precision-constant";
//    options += " -D N_ITERATIONS=" + toString( its );
    options += " -D N=" + toString( numFloats );
//    options += " -D UNROLL=" + toString( unroll );

    string kernelSource = "";
    if( kernelVersion == 1 ) {
        kernelSource = kernelSource1;
    } else if( kernelVersion == 2 ) {
        kernelSource = kernelSource2;
    } else if( kernelVersion == 3 ) {
        kernelSource = kernelSource3;
    } else if( kernelVersion == 4 ) {
        kernelSource = kernelSource4;
    } else if( kernelVersion == 5 ) {
        SpeedTemplates::Template mytemplate( kernelSource5 );
        mytemplate.setValue( "COUNT", count );
//        mytemplate.setValue( "unroll", unroll );
        string renderedSource = mytemplate.render();
        cout << "rendered source: [" << renderedSource << "]" << endl;
        kernelSource = renderedSource;
    } else if( kernelVersion == 6 ) {
        kernelSource = kernelSource6;
    } else if( kernelVersion == 7 ) {
        SpeedTemplates::Template mytemplate( kernelSource7 );
        mytemplate.setValue( "COUNT", count );
//        mytemplate.setValue( "unroll", unroll );
        string renderedSource = mytemplate.render();
        cout << "rendered source: [" << renderedSource << "]" << endl;
        kernelSource = renderedSource;
    } else {
        throw runtime_error("kernel version " + toString( kernelVersion ) + " not implemented");
    }

    CLKernel *kernel = cl->buildKernelFromString( kernelSource, "memcpy", options );

    float *src = new float[numFloats];
    for( int i = 0; i < numFloats; i++ ) {
        src[i] = 2.0f;
    }
    float *dest = new float[numFloats];
    memset( dest, 0, numFloats * sizeof(float) );
    kernel->in( numFloats, src );
    kernel->out( numFloats, dest );
    int numWorkgroups = ( numFloats + workgroupSize - 1 ) / workgroupSize;
    if( kernelVersion >= 6 ) {
        numWorkgroups = ( numFloats / 4 + workgroupSize - 1 ) / workgroupSize;
    }
    Timer timer;
    kernel->run_1d( numWorkgroups * workgroupSize, workgroupSize );
    cl->finish();
    float kernelTime = timer.lap();
    cout << "time: " << kernelTime << "ms" << endl;
//    cout << stuff[0] << " " << stuff[1] << endl;

    float kernelTimeSeconds = kernelTime / 1000.0f;

    float throughputGbytes = numFloats * 4.0f  / 1024 / 1024 / 1024;
    if( kernelVersion >= 4 ) {
        throughputGbytes *= count;
    }
    float throughputGbytespersec = throughputGbytes / kernelTimeSeconds;
//    float throughputGflops = (float)its * workgroupSize / kernelTimeSeconds / 1024 / 1024 / 1024;

    cout << "throughput: " << throughputGbytespersec << "GB/s" << endl;

    delete[] src;
    delete[] dest;
    delete kernel;
    delete cl;

    return 0;
}

<commit_msg>bug fixing for testmemperf<commit_after>#include <iostream>
#include <string>
#include <cstring>

#include "OpenCLHelper.h"
#include "stringhelper.h"
#include "Timer.h"
#include "test/TestArgsParser.h"
#include "test/SpeedTemplates.h"

using namespace std;

// for use in conjunction with http://www.cs.berkeley.edu/~volkov/volkov10-GTC.pdf
int main( int argc, char *argv[] ) {
    OpenCLHelper *cl = OpenCLHelper::createForFirstGpuOtherwiseCpu();

//    int its = 10000000;
    int numFloats = 50000000;
    int workgroupSize = 512;
    bool optimizerOn = true;
    int count = 1;
//    bool enableMad = true;
    int kernelVersion = 1;

    TestArgsParser args( argc, argv );
//    args._arg( "its", &its );
    args._arg( "n", &numFloats );
    args._arg( "workgroupsize", &workgroupSize );
    args._arg( "opt", &optimizerOn );
    args._arg( "count", &count );
    args._arg( "kernel", &kernelVersion );
//    args._arg( "mad", &enableMad );
    args._go();
    cout << "values:" << endl;
    args._printValues();

    count = OpenCLHelper::getNextPower2( count );
    int thiscount = count;
    int shift = 0;
    while( thiscount > 1 ) {
        thiscount >>= 1;
        shift++;
    }
    cout << "count: " << count << " shift: " << shift << endl;

//    cout << "its: " << its << " workgroupsize: " << workgroupSize << " optimizer: " << optimizerOn << endl;

    string kernelSource1 = R"DELIM(
        kernel void memcpy( global float const*src, global float *dest) {
            if( get_global_id(0) < N ) {
                float a = src[get_global_id(0)];
                dest[get_global_id(0)] = a;
            }
        }
    )DELIM";

    string kernelSource2 = R"DELIM(
        kernel void memcpy(global float const*src, global float *dest) {
            if( get_global_id(0) < N ) {
                dest[get_global_id(0)] = src[get_global_id(0)];
            }
        }
    )DELIM";

    string kernelSource3 = R"DELIM(
        kernel void memcpy(global float const*src, global float *dest) {
            dest[get_global_id(0)] = src[get_global_id(0)];
        }
    )DELIM";

    string kernelSource4 = R"DELIM(
        kernel void memcpy(global float const*src, global float *dest) {
            float a[COUNT];
            int offset = get_global_id(0) << SHIFT;
//            if( offset < N ) {
            if( get_global_id(0) < ( N >> SHIFT ) ) {
                #pragma unroll COUNT
                for( int i = 0; i < COUNT; i++ ) {
                    a[i] = src[ offset + i ];
                }
                #pragma unroll COUNT
                for( int i = 0; i < COUNT; i++ ) {
                    dest[ offset + i ] = a[i];
                }
            }
        }
    )DELIM";

    string kernelSource5 = R"DELIM(
        kernel void memcpy(global float const*src, global float *dest) {
            float a[COUNT];
            int offset = get_global_id(0) << {{SHIFT}};
//            if( offset < N ) {
            if( get_global_id(0) < ( N >> {{SHIFT}} ) ) {
                {% for i in range( COUNT ) %}
                    a[{{i}}] = src[ offset + {{i}} ];
                {% endfor %}
                {% for i in range( COUNT ) %}
                    dest[ offset + {{i}} ] = a[{{i}}];
                {% endfor %}
            }
        }
    )DELIM";

    string kernelSource6 = R"DELIM(
        #define f4(var) ( (global float4*)(var) )

        kernel void memcpy(global float const*src, global float *dest) {
            float4 a[COUNT];
            int offset = get_global_id(0) << SHIFT;
            if( get_global_id(0) < ( N >> SHIFT ) ) {
                #pragma unroll COUNT
                for( int i = 0; i < COUNT; i++ ) {
                    a[i] = f4(src)[ offset + i ];
                }
                #pragma unroll COUNT
                for( int i = 0; i < COUNT; i++ ) {
                    f4(dest)[ offset + i ] = a[i];
                }
            }
        }
    )DELIM";

    string kernelSource7 = R"DELIM(
        #define f4(var) ( (global float4*)(var) )

        kernel void memcpy(global float const*src, global float *dest) {
            float4 a[COUNT];
            int offset = get_global_id(0) << {{SHIFT}};
            if( get_global_id(0) < ( N >> {{SHIFT}} ) ) {
                {% for i in range( COUNT ) %}
                    a[{{i}}] = f4(src)[ offset + {{i}} ];
                {% endfor %}
                {% for i in range( COUNT ) %}
                    f4(dest)[ offset + {{i}} ] = a[{{i}}];
                {% endfor %}
            }
        }
    )DELIM";

    string options = "";
    if( !optimizerOn ) {
        options += " -cl-opt-disable";
    }
//    if( enableMad ) {
        options += " -cl-mad-enable";
//    }
    options += " -D COUNT=" + toString( count );
    options += " -D SHIFT=" + toString( shift );
//    if( kernelVersion == 1 && count == 4 ) {
//        options += " -D dOn";
//    }
    options += " -cl-single-precision-constant";
//    options += " -D N_ITERATIONS=" + toString( its );
    options += " -D N=" + toString( numFloats );
//    options += " -D UNROLL=" + toString( unroll );

    string kernelSource = "";
    if( kernelVersion == 1 ) {
        kernelSource = kernelSource1;
    } else if( kernelVersion == 2 ) {
        kernelSource = kernelSource2;
    } else if( kernelVersion == 3 ) {
        kernelSource = kernelSource3;
    } else if( kernelVersion == 4 ) {
        kernelSource = kernelSource4;
    } else if( kernelVersion == 5 ) {
        SpeedTemplates::Template mytemplate( kernelSource5 );
        mytemplate.setValue( "COUNT", count );
        mytemplate.setValue( "SHIFT", shift );
//        mytemplate.setValue( "unroll", unroll );
        string renderedSource = mytemplate.render();
        cout << "rendered source: [" << renderedSource << "]" << endl;
        kernelSource = renderedSource;
    } else if( kernelVersion == 6 ) {
        kernelSource = kernelSource6;
    } else if( kernelVersion == 7 ) {
        SpeedTemplates::Template mytemplate( kernelSource7 );
        mytemplate.setValue( "COUNT", count );
        mytemplate.setValue( "SHIFT", shift );
//        mytemplate.setValue( "unroll", unroll );
        string renderedSource = mytemplate.render();
        cout << "rendered source: [" << renderedSource << "]" << endl;
        kernelSource = renderedSource;
    } else {
        throw runtime_error("kernel version " + toString( kernelVersion ) + " not implemented");
    }

    CLKernel *kernel = cl->buildKernelFromString( kernelSource, "memcpy", options );

    float *src = new float[numFloats];
    for( int i = 0; i < numFloats; i++ ) {
        src[i] = 2.0f;
    }
    float *dest = new float[numFloats];
    memset( dest, 0, numFloats * sizeof(float) );
    kernel->in( numFloats, src );
    kernel->out( numFloats, dest );
    int numWorkgroups = ( numFloats + workgroupSize - 1 ) / workgroupSize;
    if( kernelVersion >= 6 ) {
        numWorkgroups = ( numFloats / 4 + workgroupSize - 1 ) / workgroupSize;
    }
    Timer timer;
    kernel->run_1d( numWorkgroups * workgroupSize, workgroupSize );
    cl->finish();
    float kernelTime = timer.lap();
    cout << "time: " << kernelTime << "ms" << endl;
//    cout << stuff[0] << " " << stuff[1] << endl;

    float kernelTimeSeconds = kernelTime / 1000.0f;

    float throughputGbytes = numFloats * 4.0f  / 1024 / 1024 / 1024;
    if( kernelVersion >= 4 ) {
        throughputGbytes *= count;
    }
    float throughputGbytespersec = throughputGbytes / kernelTimeSeconds;
//    float throughputGflops = (float)its * workgroupSize / kernelTimeSeconds / 1024 / 1024 / 1024;

    cout << "throughput: " << throughputGbytespersec << "GB/s" << endl;

    delete[] src;
    delete[] dest;
    delete kernel;
    delete cl;

    return 0;
}

<|endoftext|>


<commit_before>//
//  Type.c
//  Emojicode
//
//  Created by Theo Weidmann on 04.03.15.
//  Copyright (c) 2015 Theo Weidmann. All rights reserved.
//

#include <cstring>
#include <vector>
#include "utf8.h"
#include "Class.hpp"
#include "Function.hpp"
#include "EmojicodeCompiler.hpp"
#include "Enum.hpp"
#include "Protocol.hpp"
#include "TypeContext.hpp"
#include "ValueType.hpp"

//MARK: Globals
/* Very important one time declarations */

Class *CL_STRING;
Class *CL_LIST;
Class *CL_ERROR;
Class *CL_DATA;
Class *CL_DICTIONARY;
Protocol *PR_ENUMERATEABLE;
Protocol *PR_ENUMERATOR;
Class *CL_RANGE;
ValueType *VT_BOOLEAN;
ValueType *VT_SYMBOL;
ValueType *VT_INTEGER;
ValueType *VT_DOUBLE;

Type::Type(Protocol *p, bool o) : typeDefinition_(p), typeContent_(TypeContent::Protocol), optional_(o)  {
}

Type::Type(Enum *e, bool o) : typeDefinition_(e), typeContent_(TypeContent::Enum), optional_(o) {
}

Type::Type(ValueType *v, bool o) : typeDefinition_(v), typeContent_(TypeContent::ValueType), optional_(o) {
}

Type::Type(Class *c, bool o) : typeDefinition_(c), typeContent_(TypeContent::Class), optional_(o) {
    for (int i = 0; i < c->numberOfGenericArgumentsWithSuperArguments(); i++) {
        genericArguments.push_back(Type(TypeContent::Reference, false, i, c));
    }
}

Class* Type::eclass() const {
    return static_cast<Class *>(typeDefinition_);
}

Protocol* Type::protocol() const {
    return static_cast<Protocol *>(typeDefinition_);
}

Enum* Type::eenum() const {
    return static_cast<Enum *>(typeDefinition_);
}

ValueType* Type::valueType() const {
    return static_cast<ValueType *>(typeDefinition_);
}

TypeDefinition* Type::typeDefinition() const  {
    return static_cast<TypeDefinition *>(typeDefinition_);
}

bool Type::canHaveGenericArguments() const {
    return type() == TypeContent::Class || type() == TypeContent::Protocol;
}

TypeDefinitionFunctional* Type::typeDefinitionFunctional() const {
    return static_cast<TypeDefinitionFunctional *>(typeDefinition_);
}

Type Type::copyWithoutOptional() const {
    Type type = *this;
    type.optional_ = false;
    return type;
}

bool Type::allowsMetaType() {
    return type() == TypeContent::Class || type() == TypeContent::Enum || type() == TypeContent::ValueType;
}

Type Type::resolveReferenceToBaseReferenceOnSuperArguments(TypeContext typeContext) const {
    TypeDefinitionFunctional *c = typeContext.calleeType().typeDefinitionFunctional();
    Type t = *this;

    auto maxReferenceForSuper = c->numberOfGenericArgumentsWithSuperArguments() - c->numberOfOwnGenericArguments();
    // Try to resolve on the generic arguments to the superclass.
    while (t.type() == TypeContent::Reference && c->canBeUsedToResolve(c) && t.reference < maxReferenceForSuper) {
        Type tn = c->superGenericArguments()[t.reference];
        if (tn.type() != TypeContent::Reference) {
            break;
        }
    }
    return t;
}

Type Type::resolveOnSuperArgumentsAndConstraints(TypeContext typeContext, bool resolveSelf) const {
    TypeDefinitionFunctional *c = typeContext.calleeType().typeDefinitionFunctional();
    Type t = *this;
    bool optional = t.optional();

    if (resolveSelf && t.type() == TypeContent::Self) {
        t = typeContext.calleeType();
    }

    auto maxReferenceForSuper = c->numberOfGenericArgumentsWithSuperArguments() - c->numberOfOwnGenericArguments();
    // Try to resolve on the generic arguments to the superclass.
    while (t.type() == TypeContent::Reference && t.reference < maxReferenceForSuper) {
        t = c->superGenericArguments()[t.reference];
    }
    while (t.type() == TypeContent::LocalReference) {
        t = typeContext.function()->genericArgumentConstraints[t.reference];
    }
    while (t.type() == TypeContent::Reference) {
        t = typeContext.calleeType().typeDefinitionFunctional()->genericArgumentConstraints()[t.reference];
    }

    if (optional) t.setOptional();
    return t;
}

Type Type::resolveOn(TypeContext typeContext, bool resolveSelf) const {
    Type t = *this;
    bool optional = t.optional();

    if (resolveSelf && t.type() == TypeContent::Self && typeContext.calleeType().resolveSelfOn_) {
        t = typeContext.calleeType();
    }

    while (t.type() == TypeContent::LocalReference && typeContext.functionGenericArguments()) {
        t = (*typeContext.functionGenericArguments())[t.reference];
    }

    if (typeContext.calleeType().canHaveGenericArguments()) {
        while (t.type() == TypeContent::Reference &&
               typeContext.calleeType().typeDefinitionFunctional()->canBeUsedToResolve(t.resolutionConstraint_)) {
            Type tn = typeContext.calleeType().genericArguments[t.reference];
            if (tn.type() == TypeContent::Reference && tn.reference == t.reference
                && tn.resolutionConstraint_ == t.resolutionConstraint_) {
                break;
            }
            t = tn;
        }
    }

    if (optional) t.setOptional();

    if (t.canHaveGenericArguments()) {
        for (int i = 0; i < t.typeDefinitionFunctional()->numberOfGenericArgumentsWithSuperArguments(); i++) {
            t.genericArguments[i] = t.genericArguments[i].resolveOn(typeContext);
        }
    }
    else if (t.type() == TypeContent::Callable) {
        for (int i = 0; i < t.genericArguments.size(); i++) {
            t.genericArguments[i] = t.genericArguments[i].resolveOn(typeContext);
        }
    }

    return t;
}

bool Type::identicalGenericArguments(Type to, TypeContext ct, std::vector<CommonTypeFinder> *ctargs) const {
    if (to.typeDefinitionFunctional()->numberOfOwnGenericArguments()) {
        for (int l = to.typeDefinitionFunctional()->numberOfOwnGenericArguments(), i = to.typeDefinitionFunctional()->numberOfGenericArgumentsWithSuperArguments() - l; i < l; i++) {
            if (!this->genericArguments[i].identicalTo(to.genericArguments[i], ct, ctargs)) {
                return false;
            }
        }
    }
    return true;
}

bool Type::compatibleTo(Type to, TypeContext ct, std::vector<CommonTypeFinder> *ctargs) const {
    if (to.type() == TypeContent::Something) {
        return true;
    }
    if (to.meta_ != meta_) {
        return false;
    }
    if (this->optional() && !to.optional()) {
        return false;
    }

    if (to.type() == TypeContent::Someobject && this->type() == TypeContent::Class ||
        this->type() == TypeContent::Protocol || this->type() == TypeContent::Someobject) {
        return true;
    }
    if (this->type() == TypeContent::Class && to.type() == TypeContent::Class) {
        return this->eclass()->inheritsFrom(to.eclass()) && identicalGenericArguments(to, ct, ctargs);
    }
    if ((this->type() == TypeContent::Protocol && to.type() == TypeContent::Protocol) ||
        (this->type() == TypeContent::ValueType && to.type() == TypeContent::ValueType)) {
        return this->typeDefinition() == to.typeDefinition() && identicalGenericArguments(to, ct, ctargs);
    }
    if (this->type() == TypeContent::Class && to.type() == TypeContent::Protocol) {
        for (Class *a = this->eclass(); a != nullptr; a = a->superclass()) {
            for (auto protocol : a->protocols()) {
                if (protocol.resolveOn(*this).compatibleTo(to, ct, ctargs)) return true;
            }
        }
        return false;
    }
    if (this->type() == TypeContent::Nothingness) {
        return to.optional() || to.type() == TypeContent::Nothingness;
    }
    if (this->type() == TypeContent::Enum && to.type() == TypeContent::Enum) {
        return this->eenum() == to.eenum();
    }
    if ((this->type() == TypeContent::Reference && to.type() == TypeContent::Reference) ||
             (this->type() == TypeContent::LocalReference && to.type() == TypeContent::LocalReference)) {
        return this->reference == to.reference && this->resolutionConstraint_ == to.resolutionConstraint_ ||
                this->resolveOnSuperArgumentsAndConstraints(ct)
                    .compatibleTo(to.resolveOnSuperArgumentsAndConstraints(ct), ct, ctargs);
    }
    if (this->type() == TypeContent::Reference) {
        return this->resolveOnSuperArgumentsAndConstraints(ct).compatibleTo(to, ct, ctargs);
    }
    if (to.type() == TypeContent::Reference) {
        return this->compatibleTo(to.resolveOnSuperArgumentsAndConstraints(ct), ct, ctargs);
    }
    if (this->type() == TypeContent::LocalReference) {
        return ctargs || this->resolveOnSuperArgumentsAndConstraints(ct).compatibleTo(to, ct, ctargs);
    }
    if (to.type() == TypeContent::LocalReference) {
        if (ctargs) {
            (*ctargs)[to.reference].addType(*this, ct);
            return true;
        }
        else {
            return this->compatibleTo(to.resolveOnSuperArgumentsAndConstraints(ct), ct, ctargs);
        }
    }
    if (to.type() == TypeContent::Self) {
        return this->type() == to.type();
    }
    if (this->type() == TypeContent::Self) {
        return this->resolveOnSuperArgumentsAndConstraints(ct).compatibleTo(to, ct, ctargs);
    }
    if (this->type() == TypeContent::Callable && to.type() == TypeContent::Callable) {
        if (this->genericArguments[0].compatibleTo(to.genericArguments[0], ct, ctargs)
            && to.genericArguments.size() == this->genericArguments.size()) {
            for (int i = 1; i < to.genericArguments.size(); i++) {
                if (!to.genericArguments[i].compatibleTo(this->genericArguments[i], ct, ctargs)) {
                    return false;
                }
            }
            return true;
        }
        return false;
    }

    return this->type() == to.type();
}

bool Type::identicalTo(Type to, TypeContext tc, std::vector<CommonTypeFinder> *ctargs) const {
    if (ctargs && to.type() == TypeContent::LocalReference) {
        (*ctargs)[to.reference].addType(*this, tc);
        return true;
    }

    if (type() == to.type()) {
        switch (type()) {
            case TypeContent::Class:
            case TypeContent::Protocol:
            case TypeContent::ValueType:
                return typeDefinitionFunctional() == to.typeDefinitionFunctional()
                        && identicalGenericArguments(to, tc, ctargs);
            case TypeContent::Callable:
                return to.genericArguments.size() == this->genericArguments.size()
                        && identicalGenericArguments(to, tc, ctargs);
            case TypeContent::Enum:
                return eenum() == to.eenum();
            case TypeContent::Reference:
            case TypeContent::LocalReference:
                return resolveReferenceToBaseReferenceOnSuperArguments(tc).reference ==
                        to.resolveReferenceToBaseReferenceOnSuperArguments(tc).reference;
            case TypeContent::Self:
            case TypeContent::Something:
            case TypeContent::Someobject:
            case TypeContent::Nothingness:
                return true;
        }
    }
    return false;
}

//MARK: Type Visulisation

const char* Type::typePackage() {
    switch (this->type()) {
        case TypeContent::Class:
        case TypeContent::ValueType:
        case TypeContent::Protocol:
        case TypeContent::Enum:
            return this->typeDefinition()->package()->name();
        case TypeContent::Nothingness:
        case TypeContent::Something:
        case TypeContent::Someobject:
        case TypeContent::Reference:
        case TypeContent::LocalReference:
        case TypeContent::Callable:
        case TypeContent::Self:
            return "";
    }
}

void stringAppendEc(EmojicodeChar c, std::string &string) {
    ecCharToCharStack(c, sc);
    string.append(sc);
}

void Type::typeName(Type type, TypeContext typeContext, bool includePackageAndOptional, std::string &string) const {
    if (type.meta_) {
        stringAppendEc(E_WHITE_SQUARE_BUTTON, string);
    }

    if (includePackageAndOptional) {
        if (type.optional()) {
            stringAppendEc(E_CANDY, string);
        }

        string.append(type.typePackage());
    }

    switch (type.type()) {
        case TypeContent::Class:
        case TypeContent::Protocol:
        case TypeContent::Enum:
        case TypeContent::ValueType:
            stringAppendEc(type.typeDefinition()->name(), string);
            break;
        case TypeContent::Nothingness:
            stringAppendEc(E_SPARKLES, string);
            return;
        case TypeContent::Something:
            stringAppendEc(E_MEDIUM_WHITE_CIRCLE, string);
            return;
        case TypeContent::Someobject:
            stringAppendEc(E_LARGE_BLUE_CIRCLE, string);
            return;
        case TypeContent::Self:
            stringAppendEc(E_DOG, string);
            return;
        case TypeContent::Callable:
            stringAppendEc(E_GRAPES, string);

            for (int i = 1; i < type.genericArguments.size(); i++) {
                typeName(type.genericArguments[i], typeContext, includePackageAndOptional, string);
            }

            stringAppendEc(E_RIGHTWARDS_ARROW, string);
            stringAppendEc(0xFE0F, string);
            typeName(type.genericArguments[0], typeContext, includePackageAndOptional, string);
            stringAppendEc(E_WATERMELON, string);
            return;
        case TypeContent::Reference: {
            if (typeContext.calleeType().type() == TypeContent::Class) {
                Class *eclass = typeContext.calleeType().eclass();
                do {
                    for (auto it : eclass->ownGenericArgumentVariables()) {
                        if (it.second.reference == type.reference) {
                            string.append(it.first.utf8CString());
                            return;
                        }
                    }
                } while ((eclass = eclass->superclass()));
            }
            else if (typeContext.calleeType().canHaveGenericArguments()) {
                for (auto it : typeContext.calleeType().typeDefinitionFunctional()->ownGenericArgumentVariables()) {
                    if (it.second.reference == type.reference) {
                        string.append(it.first.utf8CString());
                        return;
                    }
                }
            }

            stringAppendEc('T', string);
            stringAppendEc('0' + type.reference, string);
            return;
        }
        case TypeContent::LocalReference:
            if (typeContext.function()) {
                for (auto it : typeContext.function()->genericArgumentVariables) {
                    if (it.second.reference == type.reference) {
                        string.append(it.first.utf8CString());
                        return;
                    }
                }
            }

            stringAppendEc('L', string);
            stringAppendEc('0' + type.reference, string);
            return;
    }

    if (typeContext.calleeType().type() != TypeContent::Nothingness && type.canHaveGenericArguments()) {
        auto typeDef = type.typeDefinitionFunctional();
        int offset = typeDef->numberOfGenericArgumentsWithSuperArguments() - typeDef->numberOfOwnGenericArguments();
        for (int i = 0, l = typeDef->numberOfOwnGenericArguments(); i < l; i++) {
            stringAppendEc(E_SPIRAL_SHELL, string);
            typeName(type.genericArguments[offset + i], typeContext, includePackageAndOptional, string);
        }
    }
}

std::string Type::toString(TypeContext typeContext, bool includeNsAndOptional) const {
    std::string string;
    typeName(*this, typeContext, includeNsAndOptional, string);
    return string;
}
<commit_msg>🕵 Fix compiler bug<commit_after>//
//  Type.c
//  Emojicode
//
//  Created by Theo Weidmann on 04.03.15.
//  Copyright (c) 2015 Theo Weidmann. All rights reserved.
//

#include <cstring>
#include <vector>
#include "utf8.h"
#include "Class.hpp"
#include "Function.hpp"
#include "EmojicodeCompiler.hpp"
#include "Enum.hpp"
#include "Protocol.hpp"
#include "TypeContext.hpp"
#include "ValueType.hpp"

//MARK: Globals
/* Very important one time declarations */

Class *CL_STRING;
Class *CL_LIST;
Class *CL_ERROR;
Class *CL_DATA;
Class *CL_DICTIONARY;
Protocol *PR_ENUMERATEABLE;
Protocol *PR_ENUMERATOR;
Class *CL_RANGE;
ValueType *VT_BOOLEAN;
ValueType *VT_SYMBOL;
ValueType *VT_INTEGER;
ValueType *VT_DOUBLE;

Type::Type(Protocol *p, bool o) : typeDefinition_(p), typeContent_(TypeContent::Protocol), optional_(o)  {
}

Type::Type(Enum *e, bool o) : typeDefinition_(e), typeContent_(TypeContent::Enum), optional_(o) {
}

Type::Type(ValueType *v, bool o) : typeDefinition_(v), typeContent_(TypeContent::ValueType), optional_(o) {
}

Type::Type(Class *c, bool o) : typeDefinition_(c), typeContent_(TypeContent::Class), optional_(o) {
    for (int i = 0; i < c->numberOfGenericArgumentsWithSuperArguments(); i++) {
        genericArguments.push_back(Type(TypeContent::Reference, false, i, c));
    }
}

Class* Type::eclass() const {
    return static_cast<Class *>(typeDefinition_);
}

Protocol* Type::protocol() const {
    return static_cast<Protocol *>(typeDefinition_);
}

Enum* Type::eenum() const {
    return static_cast<Enum *>(typeDefinition_);
}

ValueType* Type::valueType() const {
    return static_cast<ValueType *>(typeDefinition_);
}

TypeDefinition* Type::typeDefinition() const  {
    return static_cast<TypeDefinition *>(typeDefinition_);
}

bool Type::canHaveGenericArguments() const {
    return type() == TypeContent::Class || type() == TypeContent::Protocol;
}

TypeDefinitionFunctional* Type::typeDefinitionFunctional() const {
    return static_cast<TypeDefinitionFunctional *>(typeDefinition_);
}

Type Type::copyWithoutOptional() const {
    Type type = *this;
    type.optional_ = false;
    return type;
}

bool Type::allowsMetaType() {
    return type() == TypeContent::Class || type() == TypeContent::Enum || type() == TypeContent::ValueType;
}

Type Type::resolveReferenceToBaseReferenceOnSuperArguments(TypeContext typeContext) const {
    TypeDefinitionFunctional *c = typeContext.calleeType().typeDefinitionFunctional();
    Type t = *this;

    auto maxReferenceForSuper = c->numberOfGenericArgumentsWithSuperArguments() - c->numberOfOwnGenericArguments();
    // Try to resolve on the generic arguments to the superclass.
    while (t.type() == TypeContent::Reference && c->canBeUsedToResolve(c) && t.reference < maxReferenceForSuper) {
        Type tn = c->superGenericArguments()[t.reference];
        if (tn.type() != TypeContent::Reference) {
            break;
        }
    }
    return t;
}

Type Type::resolveOnSuperArgumentsAndConstraints(TypeContext typeContext, bool resolveSelf) const {
    if (typeContext.calleeType().type() == TypeContent::Nothingness) return *this;
    TypeDefinitionFunctional *c = typeContext.calleeType().typeDefinitionFunctional();
    Type t = *this;
    bool optional = t.optional();

    if (resolveSelf && t.type() == TypeContent::Self) {
        t = typeContext.calleeType();
    }

    auto maxReferenceForSuper = c->numberOfGenericArgumentsWithSuperArguments() - c->numberOfOwnGenericArguments();
    // Try to resolve on the generic arguments to the superclass.
    while (t.type() == TypeContent::Reference && t.reference < maxReferenceForSuper) {
        t = c->superGenericArguments()[t.reference];
    }
    while (t.type() == TypeContent::LocalReference) {
        t = typeContext.function()->genericArgumentConstraints[t.reference];
    }
    while (t.type() == TypeContent::Reference) {
        t = typeContext.calleeType().typeDefinitionFunctional()->genericArgumentConstraints()[t.reference];
    }

    if (optional) t.setOptional();
    return t;
}

Type Type::resolveOn(TypeContext typeContext, bool resolveSelf) const {
    Type t = *this;
    bool optional = t.optional();

    if (resolveSelf && t.type() == TypeContent::Self && typeContext.calleeType().resolveSelfOn_) {
        t = typeContext.calleeType();
    }

    while (t.type() == TypeContent::LocalReference && typeContext.functionGenericArguments()) {
        t = (*typeContext.functionGenericArguments())[t.reference];
    }

    if (typeContext.calleeType().canHaveGenericArguments()) {
        while (t.type() == TypeContent::Reference &&
               typeContext.calleeType().typeDefinitionFunctional()->canBeUsedToResolve(t.resolutionConstraint_)) {
            Type tn = typeContext.calleeType().genericArguments[t.reference];
            if (tn.type() == TypeContent::Reference && tn.reference == t.reference
                && tn.resolutionConstraint_ == t.resolutionConstraint_) {
                break;
            }
            t = tn;
        }
    }

    if (optional) t.setOptional();

    if (t.canHaveGenericArguments()) {
        for (int i = 0; i < t.typeDefinitionFunctional()->numberOfGenericArgumentsWithSuperArguments(); i++) {
            t.genericArguments[i] = t.genericArguments[i].resolveOn(typeContext);
        }
    }
    else if (t.type() == TypeContent::Callable) {
        for (int i = 0; i < t.genericArguments.size(); i++) {
            t.genericArguments[i] = t.genericArguments[i].resolveOn(typeContext);
        }
    }

    return t;
}

bool Type::identicalGenericArguments(Type to, TypeContext ct, std::vector<CommonTypeFinder> *ctargs) const {
    if (to.typeDefinitionFunctional()->numberOfOwnGenericArguments()) {
        for (int l = to.typeDefinitionFunctional()->numberOfOwnGenericArguments(), i = to.typeDefinitionFunctional()->numberOfGenericArgumentsWithSuperArguments() - l; i < l; i++) {
            if (!this->genericArguments[i].identicalTo(to.genericArguments[i], ct, ctargs)) {
                return false;
            }
        }
    }
    return true;
}

bool Type::compatibleTo(Type to, TypeContext ct, std::vector<CommonTypeFinder> *ctargs) const {
    if (to.type() == TypeContent::Something) {
        return true;
    }
    if (to.meta_ != meta_) {
        return false;
    }
    if (this->optional() && !to.optional()) {
        return false;
    }

    if (to.type() == TypeContent::Someobject && this->type() == TypeContent::Class ||
        this->type() == TypeContent::Protocol || this->type() == TypeContent::Someobject) {
        return true;
    }
    if (this->type() == TypeContent::Class && to.type() == TypeContent::Class) {
        return this->eclass()->inheritsFrom(to.eclass()) && identicalGenericArguments(to, ct, ctargs);
    }
    if ((this->type() == TypeContent::Protocol && to.type() == TypeContent::Protocol) ||
        (this->type() == TypeContent::ValueType && to.type() == TypeContent::ValueType)) {
        return this->typeDefinition() == to.typeDefinition() && identicalGenericArguments(to, ct, ctargs);
    }
    if (this->type() == TypeContent::Class && to.type() == TypeContent::Protocol) {
        for (Class *a = this->eclass(); a != nullptr; a = a->superclass()) {
            for (auto protocol : a->protocols()) {
                if (protocol.resolveOn(*this).compatibleTo(to, ct, ctargs)) return true;
            }
        }
        return false;
    }
    if (this->type() == TypeContent::Nothingness) {
        return to.optional() || to.type() == TypeContent::Nothingness;
    }
    if (this->type() == TypeContent::Enum && to.type() == TypeContent::Enum) {
        return this->eenum() == to.eenum();
    }
    if ((this->type() == TypeContent::Reference && to.type() == TypeContent::Reference) ||
             (this->type() == TypeContent::LocalReference && to.type() == TypeContent::LocalReference)) {
        return this->reference == to.reference && this->resolutionConstraint_ == to.resolutionConstraint_ ||
                this->resolveOnSuperArgumentsAndConstraints(ct)
                    .compatibleTo(to.resolveOnSuperArgumentsAndConstraints(ct), ct, ctargs);
    }
    if (this->type() == TypeContent::Reference) {
        return this->resolveOnSuperArgumentsAndConstraints(ct).compatibleTo(to, ct, ctargs);
    }
    if (to.type() == TypeContent::Reference) {
        return this->compatibleTo(to.resolveOnSuperArgumentsAndConstraints(ct), ct, ctargs);
    }
    if (this->type() == TypeContent::LocalReference) {
        return ctargs || this->resolveOnSuperArgumentsAndConstraints(ct).compatibleTo(to, ct, ctargs);
    }
    if (to.type() == TypeContent::LocalReference) {
        if (ctargs) {
            (*ctargs)[to.reference].addType(*this, ct);
            return true;
        }
        else {
            return this->compatibleTo(to.resolveOnSuperArgumentsAndConstraints(ct), ct, ctargs);
        }
    }
    if (to.type() == TypeContent::Self) {
        return this->type() == to.type();
    }
    if (this->type() == TypeContent::Self) {
        return this->resolveOnSuperArgumentsAndConstraints(ct).compatibleTo(to, ct, ctargs);
    }
    if (this->type() == TypeContent::Callable && to.type() == TypeContent::Callable) {
        if (this->genericArguments[0].compatibleTo(to.genericArguments[0], ct, ctargs)
            && to.genericArguments.size() == this->genericArguments.size()) {
            for (int i = 1; i < to.genericArguments.size(); i++) {
                if (!to.genericArguments[i].compatibleTo(this->genericArguments[i], ct, ctargs)) {
                    return false;
                }
            }
            return true;
        }
        return false;
    }

    return this->type() == to.type();
}

bool Type::identicalTo(Type to, TypeContext tc, std::vector<CommonTypeFinder> *ctargs) const {
    if (ctargs && to.type() == TypeContent::LocalReference) {
        (*ctargs)[to.reference].addType(*this, tc);
        return true;
    }

    if (type() == to.type()) {
        switch (type()) {
            case TypeContent::Class:
            case TypeContent::Protocol:
            case TypeContent::ValueType:
                return typeDefinitionFunctional() == to.typeDefinitionFunctional()
                        && identicalGenericArguments(to, tc, ctargs);
            case TypeContent::Callable:
                return to.genericArguments.size() == this->genericArguments.size()
                        && identicalGenericArguments(to, tc, ctargs);
            case TypeContent::Enum:
                return eenum() == to.eenum();
            case TypeContent::Reference:
            case TypeContent::LocalReference:
                return resolveReferenceToBaseReferenceOnSuperArguments(tc).reference ==
                        to.resolveReferenceToBaseReferenceOnSuperArguments(tc).reference;
            case TypeContent::Self:
            case TypeContent::Something:
            case TypeContent::Someobject:
            case TypeContent::Nothingness:
                return true;
        }
    }
    return false;
}

//MARK: Type Visulisation

const char* Type::typePackage() {
    switch (this->type()) {
        case TypeContent::Class:
        case TypeContent::ValueType:
        case TypeContent::Protocol:
        case TypeContent::Enum:
            return this->typeDefinition()->package()->name();
        case TypeContent::Nothingness:
        case TypeContent::Something:
        case TypeContent::Someobject:
        case TypeContent::Reference:
        case TypeContent::LocalReference:
        case TypeContent::Callable:
        case TypeContent::Self:
            return "";
    }
}

void stringAppendEc(EmojicodeChar c, std::string &string) {
    ecCharToCharStack(c, sc);
    string.append(sc);
}

void Type::typeName(Type type, TypeContext typeContext, bool includePackageAndOptional, std::string &string) const {
    if (type.meta_) {
        stringAppendEc(E_WHITE_SQUARE_BUTTON, string);
    }

    if (includePackageAndOptional) {
        if (type.optional()) {
            stringAppendEc(E_CANDY, string);
        }

        string.append(type.typePackage());
    }

    switch (type.type()) {
        case TypeContent::Class:
        case TypeContent::Protocol:
        case TypeContent::Enum:
        case TypeContent::ValueType:
            stringAppendEc(type.typeDefinition()->name(), string);
            break;
        case TypeContent::Nothingness:
            stringAppendEc(E_SPARKLES, string);
            return;
        case TypeContent::Something:
            stringAppendEc(E_MEDIUM_WHITE_CIRCLE, string);
            return;
        case TypeContent::Someobject:
            stringAppendEc(E_LARGE_BLUE_CIRCLE, string);
            return;
        case TypeContent::Self:
            stringAppendEc(E_DOG, string);
            return;
        case TypeContent::Callable:
            stringAppendEc(E_GRAPES, string);

            for (int i = 1; i < type.genericArguments.size(); i++) {
                typeName(type.genericArguments[i], typeContext, includePackageAndOptional, string);
            }

            stringAppendEc(E_RIGHTWARDS_ARROW, string);
            stringAppendEc(0xFE0F, string);
            typeName(type.genericArguments[0], typeContext, includePackageAndOptional, string);
            stringAppendEc(E_WATERMELON, string);
            return;
        case TypeContent::Reference: {
            if (typeContext.calleeType().type() == TypeContent::Class) {
                Class *eclass = typeContext.calleeType().eclass();
                do {
                    for (auto it : eclass->ownGenericArgumentVariables()) {
                        if (it.second.reference == type.reference) {
                            string.append(it.first.utf8CString());
                            return;
                        }
                    }
                } while ((eclass = eclass->superclass()));
            }
            else if (typeContext.calleeType().canHaveGenericArguments()) {
                for (auto it : typeContext.calleeType().typeDefinitionFunctional()->ownGenericArgumentVariables()) {
                    if (it.second.reference == type.reference) {
                        string.append(it.first.utf8CString());
                        return;
                    }
                }
            }

            stringAppendEc('T', string);
            stringAppendEc('0' + type.reference, string);
            return;
        }
        case TypeContent::LocalReference:
            if (typeContext.function()) {
                for (auto it : typeContext.function()->genericArgumentVariables) {
                    if (it.second.reference == type.reference) {
                        string.append(it.first.utf8CString());
                        return;
                    }
                }
            }

            stringAppendEc('L', string);
            stringAppendEc('0' + type.reference, string);
            return;
    }

    if (typeContext.calleeType().type() != TypeContent::Nothingness && type.canHaveGenericArguments()) {
        auto typeDef = type.typeDefinitionFunctional();
        int offset = typeDef->numberOfGenericArgumentsWithSuperArguments() - typeDef->numberOfOwnGenericArguments();
        for (int i = 0, l = typeDef->numberOfOwnGenericArguments(); i < l; i++) {
            stringAppendEc(E_SPIRAL_SHELL, string);
            typeName(type.genericArguments[offset + i], typeContext, includePackageAndOptional, string);
        }
    }
}

std::string Type::toString(TypeContext typeContext, bool includeNsAndOptional) const {
    std::string string;
    typeName(*this, typeContext, includeNsAndOptional, string);
    return string;
}
<|endoftext|>


<commit_before>
/*
  Copyright 1998-2006 FileMaker, Inc.  All Rights Reserved.

*/

#include "Support/FMPluginGlobalDefines.h"

#if defined(FMX_WIN_TARGET)
	#include "Windows.h"
#endif

#include "FMWrapper/FMXExtern.h"
#include "FMWrapper/FMXTypes.h"
#include "FMWrapper/FMXFixPt.h"
#include "FMWrapper/FMXText.h"
#include "FMWrapper/FMXData.h"
#include "FMWrapper/FMXCalcEngine.h"

#include "Support/FMPluginFunctions.h"

/****************************************************************************
 * Functions                                                                *
 ****************************************************************************/

static const unsigned long DEFAULT_FLAGS =
    fmx::ExprEnv::kMayEvaluateOnServer
  | fmx::ExprEnv::kDisplayInAllDialogs;

static struct plugin_function_definition {
  short id; const char *signature; short min, max; fmx::uint32 flags;
  fmx::errcode (
#ifdef FMX_WIN_TARGET
  __stdcall
#endif
  *callable)(short, const fmx::ExprEnv&, const fmx::DataVect&, fmx::Data&);
} plugin_functions[] = {
  {  104, "PGrp_LDistance( text1; text2 )", 2, 2, DEFAULT_FLAGS,
       &Do_PGrp_LDistance },
  {  105, "PGrp_SoundexAM( text )"        , 1, 1, DEFAULT_FLAGS,
       &Do_PGrp_SoundexAmerican },
  {  106, "PGrp_SoundexDM( text )"        , 1, 1, DEFAULT_FLAGS,
       &Do_PGrp_SoundexDM },
  {  101, "PGrp_Version"                  , 0, 0, DEFAULT_FLAGS,
       &Do_PGrp_Version },
  {  107, "PGrp_Redux( text )"            , 1, 1, DEFAULT_FLAGS,
       &Do_PGrp_Redux },
  {  102, "PGrp_ExecuteSQL( expression; columnSeparator; rowSeparator )",
                                            3, 3, DEFAULT_FLAGS,
       &Do_PGrp_ExecuteSQL },
  {    0, NULL }
};

static fmx::int32
Do_PluginInit2(fmx::int16 version)
{
    // Check the app API version
    if ((version < k70ExtnVersion) || (kMaxExtnVersion < version))
        return (kBadExtnVersion);

    // Register plug-in functions
    fmx::QuadCharAutoPtr plugin('P', 'G', 'r', 'p');
    fmx::errcode error;
    struct plugin_function_definition *cursor;

    for (cursor = plugin_functions; cursor->signature; ++cursor) {
        fmx::TextAutoPtr name, desc; const char *parenthesis;

        desc->Assign(cursor->signature, fmx::Text::kEncoding_UTF8);
        if (!(parenthesis = strchr(cursor->signature, '(')))
            name->Assign(cursor->signature, fmx::Text::kEncoding_UTF8);
        else
            name->AssignWithLength(cursor->signature, parenthesis -
                    cursor->signature, fmx::Text::kEncoding_UTF8);
        if ((error = fmx::ExprEnv::RegisterExternalFunction(*plugin,
                cursor->id, *name, *desc, cursor->min, cursor->max,
                cursor->flags, cursor->callable))) {
            while (cursor-- != plugin_functions)
                fmx::ExprEnv::UnRegisterExternalFunction(*plugin, cursor->id);
            return kDoNotEnable;
        }
    }
    return kCurrentExtnVersion;
}

static void
Do_PluginShutdown2(void)
{
    fmx::QuadCharAutoPtr plugin('P', 'G', 'r', 'p');
    plugin_function_definition *cursor;

    for (cursor = plugin_functions; cursor->signature; ++cursor)
        fmx::ExprEnv::UnRegisterExternalFunction(*plugin, cursor->id);
}

// ====== "FMPluginTahoma.h" =================================================================================
// Header-like information for FMPlugInTahoma.cpp

// General String IDs

	// The following strings are usually not localized, but it is your option.
	enum
	{
			kPGrp_AddStringID = 102,
			kPGrp_AppendStringID = 103,
			kPGrp_EvaluateStringID = 104,
			kPGrp_StartScriptStringID = 105,
			kPGrp_UserFormatNumberStringID = 106,
			kPGrp_FormatNumberStringID = 107,
			kPGrp_NumToWordsStringID = 108,
			kPGrp_VersionStringID = 109,
			kPGrp_LDistanceStringID = 110,
			kPGrp_SoundexAmericanStringID = 111,
			kPGrp_SoundexDMStringID = 112,
			kPGrp_ExecuteSQLStringID = 113,
			kPGrp_ReduxStringID = 114
	};

	// The following strings are usually localized, but it is your option.
	enum { PGrp_PluginNameStringID = 128, kPGrp_PluginDescStringID = 129 };

	FMX_ExternCallPtr	gFMX_ExternCallPtr=NULL;

static const char* plugin_strings[] = {
    "PGrp"   /* Plugin-ID */
       "1"   /* fixed value */
       "n"   /* no configuration */
       "n"   /* no old API */
       "Y"   /* fixed value */
       "n"   /* no idle time */
       "nn", /* fixed value */
    "Tahoma",
    "Tahoma String Utilities plug-in. This plug-in was derived from the "
        "FMExample plug-in."
};

static void
Do_GetString2(fmx::uchar type, fmx::ptrtype limit, fmx::unichar *target)
{
    const char *source; fmx::ptrtype i;

    switch (type) {
        case kFMXT_OptionsStr  : source = plugin_strings[0]; break;
        case kFMXT_NameStr     : source = plugin_strings[1]; break;
        case kFMXT_AppConfigStr: source = plugin_strings[2]; break;
    }
    for (i = 0; i < limit && source[i] != '\0'; ++i)
        target[i] = source[i];
}

void FMX_ENTRYPT
FMExternCallProc(FMX_ExternCallPtr pb)
{
    // Setup global defined in FMxExtern.h
    gFMX_ExternCallPtr = pb;

    // Message dispatcher
    switch (gFMX_ExternCallPtr->whichCall) {
        case kFMXT_Init:
            gFMX_ExternCallPtr->result = Do_PluginInit2(
                    gFMX_ExternCallPtr->extnVersion);
            break;
        case kFMXT_Shutdown:
            Do_PluginShutdown2();
            break;
        case kFMXT_GetString:
            Do_GetString2(pb->parm1, pb->parm3,
                reinterpret_cast<FMX_Unichar*>(pb->result));
           break;
    }
}

<commit_msg>Fix function IDs.<commit_after>
/*
  Copyright 1998-2006 FileMaker, Inc.  All Rights Reserved.

*/

#include "Support/FMPluginGlobalDefines.h"

#if defined(FMX_WIN_TARGET)
	#include "Windows.h"
#endif

#include "FMWrapper/FMXExtern.h"
#include "FMWrapper/FMXTypes.h"
#include "FMWrapper/FMXFixPt.h"
#include "FMWrapper/FMXText.h"
#include "FMWrapper/FMXData.h"
#include "FMWrapper/FMXCalcEngine.h"

#include "Support/FMPluginFunctions.h"

/****************************************************************************
 * Functions                                                                *
 ****************************************************************************/

static const unsigned long DEFAULT_FLAGS =
    fmx::ExprEnv::kMayEvaluateOnServer
  | fmx::ExprEnv::kDisplayInAllDialogs;

static struct plugin_function_definition {
  short id; const char *signature; short min, max; fmx::uint32 flags;
  fmx::errcode (
#ifdef FMX_WIN_TARGET
  __stdcall
#endif
  *callable)(short, const fmx::ExprEnv&, const fmx::DataVect&, fmx::Data&);
} plugin_functions[] = {
  {    4, "PGrp_LDistance( text1; text2 )", 2, 2, DEFAULT_FLAGS,
       &Do_PGrp_LDistance },
  {    5, "PGrp_SoundexAM( text )"        , 1, 1, DEFAULT_FLAGS,
       &Do_PGrp_SoundexAmerican },
  {    6, "PGrp_SoundexDM( text )"        , 1, 1, DEFAULT_FLAGS,
       &Do_PGrp_SoundexDM },
  {    1, "PGrp_Version"                  , 0, 0, DEFAULT_FLAGS,
       &Do_PGrp_Version },
  {    7, "PGrp_Redux( text )"            , 1, 1, DEFAULT_FLAGS,
       &Do_PGrp_Redux },
  {    2, "PGrp_ExecuteSQL( expression )",
                                            1, 1, DEFAULT_FLAGS,
       &Do_PGrp_ExecuteSQL },
  {    3, "PGrp_ExecuteScript( filename; scriptname {; parameter} )",
                                            2, 3, DEFAULT_FLAGS,
       &Do_PGrp_StartScript },
  {    0, NULL }
};

static fmx::int32
Do_PluginInit2(fmx::int16 version)
{
    // Check the app API version
    if ((version < k70ExtnVersion) || (kMaxExtnVersion < version))
        return (kBadExtnVersion);

    // Register plug-in functions
    fmx::QuadCharAutoPtr plugin('P', 'G', 'r', 'p');
    fmx::errcode error;
    struct plugin_function_definition *cursor;

    for (cursor = plugin_functions; cursor->signature; ++cursor) {
        fmx::TextAutoPtr name, desc; const char *parenthesis;

        desc->Assign(cursor->signature, fmx::Text::kEncoding_UTF8);
        if (!(parenthesis = strchr(cursor->signature, '(')))
            name->Assign(cursor->signature, fmx::Text::kEncoding_UTF8);
        else
            name->AssignWithLength(cursor->signature, parenthesis -
                    cursor->signature, fmx::Text::kEncoding_UTF8);
        if ((error = fmx::ExprEnv::RegisterExternalFunction(*plugin,
                cursor->id, *name, *desc, cursor->min, cursor->max,
                cursor->flags, cursor->callable))) {
            while (cursor-- != plugin_functions)
                fmx::ExprEnv::UnRegisterExternalFunction(*plugin, cursor->id);
            return kDoNotEnable;
        }
    }
    return kCurrentExtnVersion;
}

static void
Do_PluginShutdown2(void)
{
    fmx::QuadCharAutoPtr plugin('P', 'G', 'r', 'p');
    plugin_function_definition *cursor;

    for (cursor = plugin_functions; cursor->signature; ++cursor)
        fmx::ExprEnv::UnRegisterExternalFunction(*plugin, cursor->id);
}

// ====== "FMPluginTahoma.h" =================================================================================
// Header-like information for FMPlugInTahoma.cpp

// General String IDs

	// The following strings are usually not localized, but it is your option.
	enum
	{
			kPGrp_AddStringID = 102,
			kPGrp_AppendStringID = 103,
			kPGrp_EvaluateStringID = 104,
			kPGrp_StartScriptStringID = 105,
			kPGrp_UserFormatNumberStringID = 106,
			kPGrp_FormatNumberStringID = 107,
			kPGrp_NumToWordsStringID = 108,
			kPGrp_VersionStringID = 109,
			kPGrp_LDistanceStringID = 110,
			kPGrp_SoundexAmericanStringID = 111,
			kPGrp_SoundexDMStringID = 112,
			kPGrp_ExecuteSQLStringID = 113,
			kPGrp_ReduxStringID = 114
	};

	// The following strings are usually localized, but it is your option.
	enum { PGrp_PluginNameStringID = 128, kPGrp_PluginDescStringID = 129 };

	FMX_ExternCallPtr	gFMX_ExternCallPtr=NULL;

static const char* plugin_strings[] = {
    "PGrp"   /* Plugin-ID */
       "1"   /* fixed value */
       "n"   /* no configuration */
       "n"   /* no old API */
       "Y"   /* fixed value */
       "n"   /* no idle time */
       "nn", /* fixed value */
    "Tahoma",
    "Tahoma String Utilities plug-in. This plug-in was derived from the "
        "FMExample plug-in."
};

static void
Do_GetString2(fmx::uchar type, fmx::ptrtype limit, fmx::unichar *target)
{
    const char *source; fmx::ptrtype i;

    switch (type) {
        case kFMXT_OptionsStr  : source = plugin_strings[0]; break;
        case kFMXT_NameStr     : source = plugin_strings[1]; break;
        case kFMXT_AppConfigStr: source = plugin_strings[2]; break;
    }
    for (i = 0; i < limit && source[i] != '\0'; ++i)
        target[i] = source[i];
}

void FMX_ENTRYPT
FMExternCallProc(FMX_ExternCallPtr pb)
{
    // Setup global defined in FMxExtern.h
    gFMX_ExternCallPtr = pb;

    // Message dispatcher
    switch (gFMX_ExternCallPtr->whichCall) {
        case kFMXT_Init:
            gFMX_ExternCallPtr->result = Do_PluginInit2(
                    gFMX_ExternCallPtr->extnVersion);
            break;
        case kFMXT_Shutdown:
            Do_PluginShutdown2();
            break;
        case kFMXT_GetString:
            Do_GetString2(pb->parm1, pb->parm3,
                reinterpret_cast<FMX_Unichar*>(pb->result));
           break;
    }
}

<|endoftext|>


<commit_before>// vim: set sts=2 sw=2 et:
// encoding: utf-8
//
// Copyleft 2012 RIME Developers
// License: GPLv3
//
// 2012-06-05 GONG Chen <chen.sst@gmail.com>
//
#include <boost/foreach.hpp>
#include <rime/common.h>
#include <rime/composition.h>
#include <rime/config.h>
#include <rime/context.h>
#include <rime/engine.h>
#include <rime/key_event.h>
#include <rime/schema.h>
#include <rime/impl/chord_composer.h>

// U+FEFF works better with MacType
static const char* kZeroWidthSpace = "\xef\xbb\xbf";  //"\xe2\x80\x8b";

namespace rime {

ChordComposer::ChordComposer(Engine *engine) : Processor(engine),
                                               pass_thru_(false) {
  Config *config = engine->schema()->config();
  if (config) {
    config->GetString("chord_composer/alphabet", &alphabet_);
    config->GetString("speller/delimiter", &delimiter_);
    algebra_.Load(config->GetList("chord_composer/algebra"));
    output_format_.Load(config->GetList("chord_composer/output_format"));
    prompt_format_.Load(config->GetList("chord_composer/prompt_format"));
  }
}

Processor::Result ChordComposer::ProcessKeyEvent(const KeyEvent &key_event) {
  if (pass_thru_)
    return kNoop;
  bool composing = !chord_.empty();
  if (key_event.shift() || key_event.ctrl() || key_event.alt()) {
    ClearChord();
    return composing ? kAccepted : kNoop;
  }
  bool is_key_up = key_event.release();
  int ch = key_event.keycode();
  if (!composing && ch == XK_BackSpace && !is_key_up) {
    if (DeleteLastSyllable())
      return kAccepted;
  }
  if (alphabet_.find(ch) == std::string::npos) {
    ClearChord();
    return composing ? kAccepted : kNoop;
  }
  // in alphabet
  if (is_key_up) {
    if (pressed_.erase(ch) != 0 && pressed_.empty()) {
      FinishChord();
    }
  }
  else {  // key down
    pressed_.insert(ch);
    bool updated = chord_.insert(ch).second;
    if (updated)
      UpdateChord();
  }
  return kAccepted;
}

const std::string ChordComposer::SerializeChord() {
  std::string code;
  BOOST_FOREACH(char ch, alphabet_) {
    if (chord_.find(ch) != chord_.end())
      code.push_back(ch);
  }
  algebra_.Apply(&code);
  return code;
}

void ChordComposer::UpdateChord() {
  if (!engine_) return;
  Context* ctx = engine_->context();
  Composition* comp = ctx->composition();
  bool chord_exists = !comp->empty() && comp->back().HasTag("chord");
  bool chord_prompt = !comp->empty() && comp->back().HasTag("chord_prompt");
  if (chord_.empty()) {
    if (chord_exists) {
      ctx->ClearPreviousSegment();
    }
    else if (chord_prompt) {
      comp->back().prompt.clear();
      comp->back().tags.erase("chord_prompt");
    }
  }
  else {
    std::string code(SerializeChord());
    prompt_format_.Apply(&code);
    if (!chord_exists && !chord_prompt) {
      if (comp->empty()) {
        comp->Forward();
        ctx->PushInput(kZeroWidthSpace);
        comp->back().tags.insert("chord");
      }
      else {
        comp->back().tags.insert("chord_prompt");
      }
    }
    comp->back().prompt = code;
  }
}

void ChordComposer::FinishChord() {
  if (!engine_) return;
  std::string code(SerializeChord());
  output_format_.Apply(&code);
  ClearChord();

  KeySequence sequence;
  if (sequence.Parse(code)) {
    pass_thru_ = true;
    BOOST_FOREACH(const KeyEvent& ke, sequence) {
      engine_->ProcessKeyEvent(ke);
    }
    pass_thru_ = false;
  }
}

void ChordComposer::ClearChord() {
    pressed_.clear();
    chord_.clear();
    UpdateChord();
}

bool ChordComposer::DeleteLastSyllable() {
  if (!engine_)
    return false;
  Context* ctx = engine_->context();
  Composition* comp = ctx->composition();
  const std::string input(ctx->input());
  size_t start = comp->empty() ? 0 : comp->back().start;
  size_t caret_pos = ctx->caret_pos();
  if (input.empty() || caret_pos <= start)
    return false;
  for (size_t deleted = 0; caret_pos > start; --caret_pos, ++deleted) {
    if (deleted > 0 &&
        delimiter_.find(input[caret_pos - 1]) != std::string::npos)
      break;
    ctx->PopInput();
  }
  return true;
}

}  // namespace rime
<commit_msg>Directly commit unhandled characters in chord output.<commit_after>// vim: set sts=2 sw=2 et:
// encoding: utf-8
//
// Copyleft 2012 RIME Developers
// License: GPLv3
//
// 2012-06-05 GONG Chen <chen.sst@gmail.com>
//
#include <boost/foreach.hpp>
#include <rime/common.h>
#include <rime/composition.h>
#include <rime/config.h>
#include <rime/context.h>
#include <rime/engine.h>
#include <rime/key_event.h>
#include <rime/schema.h>
#include <rime/impl/chord_composer.h>

// U+FEFF works better with MacType
static const char* kZeroWidthSpace = "\xef\xbb\xbf";  //"\xe2\x80\x8b";

namespace rime {

ChordComposer::ChordComposer(Engine *engine) : Processor(engine),
                                               pass_thru_(false) {
  Config *config = engine->schema()->config();
  if (config) {
    config->GetString("chord_composer/alphabet", &alphabet_);
    config->GetString("speller/delimiter", &delimiter_);
    algebra_.Load(config->GetList("chord_composer/algebra"));
    output_format_.Load(config->GetList("chord_composer/output_format"));
    prompt_format_.Load(config->GetList("chord_composer/prompt_format"));
  }
}

Processor::Result ChordComposer::ProcessKeyEvent(const KeyEvent &key_event) {
  if (pass_thru_)
    return kNoop;
  bool composing = !chord_.empty();
  if (key_event.shift() || key_event.ctrl() || key_event.alt()) {
    ClearChord();
    return composing ? kAccepted : kNoop;
  }
  bool is_key_up = key_event.release();
  int ch = key_event.keycode();
  if (!composing && ch == XK_BackSpace && !is_key_up) {
    if (DeleteLastSyllable())
      return kAccepted;
  }
  if (alphabet_.find(ch) == std::string::npos) {
    ClearChord();
    return composing ? kAccepted : kNoop;
  }
  // in alphabet
  if (is_key_up) {
    if (pressed_.erase(ch) != 0 && pressed_.empty()) {
      FinishChord();
    }
  }
  else {  // key down
    pressed_.insert(ch);
    bool updated = chord_.insert(ch).second;
    if (updated)
      UpdateChord();
  }
  return kAccepted;
}

const std::string ChordComposer::SerializeChord() {
  std::string code;
  BOOST_FOREACH(char ch, alphabet_) {
    if (chord_.find(ch) != chord_.end())
      code.push_back(ch);
  }
  algebra_.Apply(&code);
  return code;
}

void ChordComposer::UpdateChord() {
  if (!engine_) return;
  Context* ctx = engine_->context();
  Composition* comp = ctx->composition();
  bool chord_exists = !comp->empty() && comp->back().HasTag("chord");
  bool chord_prompt = !comp->empty() && comp->back().HasTag("chord_prompt");
  if (chord_.empty()) {
    if (chord_exists) {
      ctx->ClearPreviousSegment();
    }
    else if (chord_prompt) {
      comp->back().prompt.clear();
      comp->back().tags.erase("chord_prompt");
    }
  }
  else {
    std::string code(SerializeChord());
    prompt_format_.Apply(&code);
    if (!chord_exists && !chord_prompt) {
      if (comp->empty()) {
        comp->Forward();
        ctx->PushInput(kZeroWidthSpace);
        comp->back().tags.insert("chord");
      }
      else {
        comp->back().tags.insert("chord_prompt");
      }
    }
    comp->back().prompt = code;
  }
}

void ChordComposer::FinishChord() {
  if (!engine_) return;
  std::string code(SerializeChord());
  output_format_.Apply(&code);
  ClearChord();

  KeySequence sequence;
  if (sequence.Parse(code)) {
    pass_thru_ = true;
    BOOST_FOREACH(const KeyEvent& ke, sequence) {
      if (!engine_->ProcessKeyEvent(ke)) {
        // direct commit
        engine_->sink()(std::string(1, ke.keycode()));
      }
    }
    pass_thru_ = false;
  }
}

void ChordComposer::ClearChord() {
    pressed_.clear();
    chord_.clear();
    UpdateChord();
}

bool ChordComposer::DeleteLastSyllable() {
  if (!engine_)
    return false;
  Context* ctx = engine_->context();
  Composition* comp = ctx->composition();
  const std::string input(ctx->input());
  size_t start = comp->empty() ? 0 : comp->back().start;
  size_t caret_pos = ctx->caret_pos();
  if (input.empty() || caret_pos <= start)
    return false;
  for (size_t deleted = 0; caret_pos > start; --caret_pos, ++deleted) {
    if (deleted > 0 &&
        delimiter_.find(input[caret_pos - 1]) != std::string::npos)
      break;
    ctx->PopInput();
  }
  return true;
}

}  // namespace rime
<|endoftext|>