static Image *ReadDCMImage(const ImageInfo *image_info,ExceptionInfo *exception)
{
#define ThrowDCMException(exception,message) \
{ \
  RelinquishDCMMemory(&info,&map,stream_info,stack,data); \
  if (info_copy != (DCMInfo *) NULL) \
    info_copy=(DCMInfo *) RelinquishDCMInfo(info_copy); \
  ThrowReaderException((exception),(message)); \
}

  char
    explicit_vr[MagickPathExtent],
    implicit_vr[MagickPathExtent],
    magick[MagickPathExtent],
    photometric[MagickPathExtent];

  DCMInfo
    info,
    *info_copy = (DCMInfo *) NULL;

  DCMMap
    map;

  DCMStreamInfo
    *stream_info;

  Image
    *image;

  int
    datum;

  LinkedListInfo
    *stack;

  MagickBooleanType
    explicit_file,
    explicit_retry,
    use_explicit;

  MagickOffsetType
    blob_size,
    offset;

  unsigned char
    *p;

  ssize_t
    i;

  size_t
    colors,
    length,
    number_scenes,
    quantum,
    status;

  ssize_t
    count,
    scene,
    sequence_depth;

  unsigned char
    *data;

  unsigned short
    group,
    element;

  /*
    Open image file.
  */
  assert(image_info != (const ImageInfo *) NULL);
  assert(image_info->signature == MagickCoreSignature);
  if (image_info->debug != MagickFalse)
    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
      image_info->filename);
  assert(exception != (ExceptionInfo *) NULL);
  assert(exception->signature == MagickCoreSignature);
  image=AcquireImage(image_info,exception);
  status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
  if (status == MagickFalse)
    {
      image=DestroyImageList(image);
      return((Image *) NULL);
    }
  image->depth=8UL;
  image->endian=LSBEndian;
  /*
    Read DCM preamble.
  */
  (void) memset(&info,0,sizeof(info));
  (void) memset(&map,0,sizeof(map));
  data=(unsigned char *) NULL;
  stream_info=(DCMStreamInfo *) AcquireMagickMemory(sizeof(*stream_info));
  sequence_depth=0;
  stack=NewLinkedList(256);
  if (stream_info == (DCMStreamInfo *) NULL)
    ThrowDCMException(ResourceLimitError,"MemoryAllocationFailed")
  (void) memset(stream_info,0,sizeof(*stream_info));
  count=ReadBlob(image,128,(unsigned char *) magick);
  if (count != 128)
    ThrowDCMException(CorruptImageError,"ImproperImageHeader")
  count=ReadBlob(image,4,(unsigned char *) magick);
  if ((count != 4) || (LocaleNCompare(magick,"DICM",4) != 0))
    {
      offset=SeekBlob(image,0L,SEEK_SET);
      if (offset < 0)
        ThrowDCMException(CorruptImageError,"ImproperImageHeader")
    }
  /*
    Read DCM Medical image.
  */
  (void) CopyMagickString(photometric,"MONOCHROME1 ",MagickPathExtent);
  info.bits_allocated=8;
  info.bytes_per_pixel=1;
  info.depth=8;
  info.mask=0xffff;
  info.max_value=255UL;
  info.samples_per_pixel=1;
  info.signed_data=(~0UL);
  info.rescale_slope=1.0;
  element=0;
  explicit_vr[2]='\0';
  explicit_file=MagickFalse;
  colors=0;
  number_scenes=1;
  use_explicit=MagickFalse;
  explicit_retry=MagickFalse;
  blob_size=(MagickOffsetType) GetBlobSize(image);
  while (TellBlob(image) < blob_size)
  {
    for (group=0; (group != 0x7FE0) || (element != 0x0010) ; )
    {
      /*
        Read a group.
      */
      image->offset=(ssize_t) TellBlob(image);
      group=ReadBlobLSBShort(image);
      element=ReadBlobLSBShort(image);
      if ((group == 0xfffc) && (element == 0xfffc))
        break;
      if ((group != 0x0002) && (image->endian == MSBEndian))
        {
          group=(unsigned short) ((group << 8) | ((group >> 8) & 0xFF));
          element=(unsigned short) ((element << 8) | ((element >> 8) & 0xFF));
        }
      quantum=0;
      /*
        Find corresponding VR for this group and element.
      */
      for (i=0; dicom_info[i].group < 0xffff; i++)
        if ((group == dicom_info[i].group) &&
            (element == dicom_info[i].element))
          break;
      (void) CopyMagickString(implicit_vr,dicom_info[i].vr,MagickPathExtent);
      count=ReadBlob(image,2,(unsigned char *) explicit_vr);
      if (count != 2)
        ThrowDCMException(CorruptImageError,"ImproperImageHeader")
      /*
        Check for "explicitness", but meta-file headers always explicit.
      */
      if ((explicit_file == MagickFalse) && (group != 0x0002))
        explicit_file=(isupper((int) ((unsigned char) *explicit_vr)) != 0) &&
          (isupper((int) ((unsigned char) *(explicit_vr+1))) != 0) ?
          MagickTrue : MagickFalse;
      use_explicit=((group == 0x0002) && (explicit_retry == MagickFalse)) ||
        (explicit_file != MagickFalse) ? MagickTrue : MagickFalse;
      if ((use_explicit != MagickFalse) && (strncmp(implicit_vr,"xs",2) == 0))
        (void) CopyMagickString(implicit_vr,explicit_vr,MagickPathExtent);
      if ((use_explicit == MagickFalse) || (strncmp(implicit_vr,"!!",2) == 0))
        {
          offset=SeekBlob(image,(MagickOffsetType) -2,SEEK_CUR);
          if (offset < 0)
            ThrowDCMException(CorruptImageError,"ImproperImageHeader")
          quantum=4;
        }
      else
        {
          /*
            Assume explicit type.
          */
          quantum=2;
          if ((strcmp(explicit_vr,"OB") == 0) ||
              (strcmp(explicit_vr,"OW") == 0) ||
              (strcmp(explicit_vr,"OF") == 0) ||
              (strcmp(explicit_vr,"SQ") == 0) ||
              (strcmp(explicit_vr,"UN") == 0) ||
              (strcmp(explicit_vr,"UT") == 0))
            {
              (void) ReadBlobLSBShort(image);
              quantum=4;
            }
        }
      if ((group == 0xFFFE) && (element == 0xE0DD))
        {
          /*
            If we're exiting a sequence, restore the previous image parameters,
            effectively undoing any parameter changes that happened inside the
            sequence.
          */
          sequence_depth--;
          info_copy=(DCMInfo *) RemoveLastElementFromLinkedList(stack);
          if (info_copy == (DCMInfo *)NULL)
            {
              /*
                The sequence's entry and exit points don't line up (tried to
                exit one more sequence than we entered).
              */
              ThrowDCMException(CorruptImageError,"ImproperImageHeader")
            }
          if (info.scale != (Quantum *) NULL)
            info.scale=(Quantum *) RelinquishMagickMemory(info.scale);
          (void) memcpy(&info,info_copy,sizeof(info));
          info_copy=(DCMInfo *) RelinquishMagickMemory(info_copy);
        }
      if (strcmp(explicit_vr,"SQ") == 0)
        {
          /*
            If we're entering a sequence, push the current image parameters
            onto the stack, so we can restore them at the end of the sequence.
          */
          DCMInfo *clone_info = (DCMInfo *) AcquireMagickMemory(sizeof(info));
          if (clone_info == (DCMInfo *) NULL)
            ThrowDCMException(ResourceLimitError,"MemoryAllocationFailed")
          (void) memcpy(clone_info,&info,sizeof(info));
          clone_info->scale=(Quantum *) AcquireQuantumMemory(
            clone_info->scale_size+1,sizeof(*clone_info->scale));
          if (clone_info->scale == (Quantum *) NULL)
            ThrowDCMException(ResourceLimitError,"MemoryAllocationFailed")
          (void) memcpy(clone_info->scale,info.scale,clone_info->scale_size*
            sizeof(*clone_info->scale));
          AppendValueToLinkedList(stack,clone_info);
          sequence_depth++;
        }
      datum=0;
      if (quantum == 4)
        {
          if (group == 0x0002)
            datum=ReadBlobLSBSignedLong(image);
          else
            datum=ReadBlobSignedLong(image);
        }
      else
        if (quantum == 2)
          {
            if (group == 0x0002)
              datum=ReadBlobLSBSignedShort(image);
            else
              datum=ReadBlobSignedShort(image);
          }
      quantum=0;
      length=1;
      if (datum != 0)
        {
          if ((strncmp(implicit_vr,"OW",2) == 0) ||
              (strncmp(implicit_vr,"SS",2) == 0) ||
              (strncmp(implicit_vr,"US",2) == 0))
            quantum=2;
          else
            if ((strncmp(implicit_vr,"FL",2) == 0) ||
                (strncmp(implicit_vr,"OF",2) == 0) ||
                (strncmp(implicit_vr,"SL",2) == 0) ||
                (strncmp(implicit_vr,"UL",2) == 0))
              quantum=4;
            else
              if (strncmp(implicit_vr,"FD",2) == 0)
                quantum=8;
              else
                quantum=1;
          if (datum != ~0)
            length=(size_t) datum/quantum;
          else
            {
              /*
                Sequence and item of undefined length.
              */
              quantum=0;
              length=0;
            }
        }
      if (image_info->verbose != MagickFalse)
        {
          /*
            Display Dicom info.
          */
          if (use_explicit == MagickFalse)
            explicit_vr[0]='\0';
          for (i=0; dicom_info[i].description != (char *) NULL; i++)
            if ((group == dicom_info[i].group) &&
                (element == dicom_info[i].element))
              break;
          (void) FormatLocaleFile(stdout,
            "0x%04lX %4ld S%ld %s-%s (0x%04lx,0x%04lx)",
            (unsigned long) image->offset,(long) length,(long) sequence_depth,
            implicit_vr,explicit_vr,(unsigned long) group,
            (unsigned long) element);
          if (dicom_info[i].description != (char *) NULL)
            (void) FormatLocaleFile(stdout," %s",dicom_info[i].description);
          (void) FormatLocaleFile(stdout,": ");
        }
      if ((group == 0x7FE0) && (element == 0x0010))
        {
          if (image_info->verbose != MagickFalse)
            (void) FormatLocaleFile(stdout,"\n");
          break;
        }
      /*
        Allocate space and read an array.
      */
      data=(unsigned char *) NULL;
      if ((length == 1) && (quantum == 1))
        datum=ReadBlobByte(image);
      else
        if ((length == 1) && (quantum == 2))
          {
            if (group == 0x0002)
              datum=ReadBlobLSBSignedShort(image);
            else
              datum=ReadBlobSignedShort(image);
          }
        else
          if ((length == 1) && (quantum == 4))
            {
              if (group == 0x0002)
                datum=ReadBlobLSBSignedLong(image);
              else
                datum=ReadBlobSignedLong(image);
            }
          else
            if ((quantum != 0) && (length != 0))
              {
                if (length > (size_t) GetBlobSize(image))
                  ThrowDCMException(CorruptImageError,
                    "InsufficientImageDataInFile")
                if (~length >= 1)
                  data=(unsigned char *) AcquireQuantumMemory(length+1,quantum*
                    sizeof(*data));
                if (data == (unsigned char *) NULL)
                  ThrowDCMException(ResourceLimitError,
                    "MemoryAllocationFailed")
                count=ReadBlob(image,(size_t) quantum*length,data);
                if (count != (ssize_t) (quantum*length))
                  {
                    if (image_info->verbose != MagickFalse)
                      (void) FormatLocaleFile(stdout,"count=%d quantum=%d "
                        "length=%d group=%d\n",(int) count,(int) quantum,(int)
                        length,(int) group);
                     ThrowDCMException(CorruptImageError,
                       "InsufficientImageDataInFile")
                  }
                data[length*quantum]='\0';
              }
      if ((((unsigned int) group << 16) | element) == 0xFFFEE0DD)
        {
          if (data != (unsigned char *) NULL)
            data=(unsigned char *) RelinquishMagickMemory(data);
          continue;
        }
      switch (group)
      {
        case 0x0002:
        {
          switch (element)
          {
            case 0x0010:
            {
              char
                transfer_syntax[MagickPathExtent];

              /*
                Transfer Syntax.
              */
              if ((datum == 0) && (explicit_retry == MagickFalse))
                {
                  explicit_retry=MagickTrue;
                  (void) SeekBlob(image,(MagickOffsetType) 0,SEEK_SET);
                  group=0;
                  element=0;
                  if (image_info->verbose != MagickFalse)
                    (void) FormatLocaleFile(stdout,
                      "Corrupted image - trying explicit format\n");
                  break;
                }
              *transfer_syntax='\0';
              if (data != (unsigned char *) NULL)
                (void) CopyMagickString(transfer_syntax,(char *) data,
                  MagickPathExtent);
              if (image_info->verbose != MagickFalse)
                (void) FormatLocaleFile(stdout,"transfer_syntax=%s\n",
                  (const char *) transfer_syntax);
              if (strncmp(transfer_syntax,"1.2.840.10008.1.2",17) == 0)
                {
                  int
                    subtype,
                    type;

                  type=1;
                  subtype=0;
                  if (strlen(transfer_syntax) > 17)
                    {
                      count=(ssize_t) sscanf(transfer_syntax+17,".%d.%d",&type,
                        &subtype);
                      if (count < 1)
                        ThrowDCMException(CorruptImageError,
                          "ImproperImageHeader")
                    }
                  switch (type)
                  {
                    case 1:
                    {
                      image->endian=LSBEndian;
                      break;
                    }
                    case 2:
                    {
                      image->endian=MSBEndian;
                      break;
                    }
                    case 4:
                    {
                      if ((subtype >= 80) && (subtype <= 81))
                        image->compression=JPEGCompression;
                      else
                        if ((subtype >= 90) && (subtype <= 93))
                          image->compression=JPEG2000Compression;
                        else
                          image->compression=JPEGCompression;
                      break;
                    }
                    case 5:
                    {
                      image->compression=RLECompression;
                      break;
                    }
                  }
                }
              break;
            }
            default:
              break;
          }
          break;
        }
        case 0x0028:
        {
          switch (element)
          {
            case 0x0002:
            {
              /*
                Samples per pixel.
              */
              info.samples_per_pixel=(size_t) datum;
              if ((info.samples_per_pixel == 0) || (info.samples_per_pixel > 4))
                ThrowDCMException(CorruptImageError,"ImproperImageHeader")
              break;
            }
            case 0x0004:
            {
              /*
                Photometric interpretation.
              */
              if (data == (unsigned char *) NULL)
                break;
              for (i=0; i < (ssize_t) MagickMin(length,MagickPathExtent-1); i++)
                photometric[i]=(char) data[i];
              photometric[i]='\0';
              info.polarity=LocaleCompare(photometric,"MONOCHROME1 ") == 0 ?
                MagickTrue : MagickFalse;
              break;
            }
            case 0x0006:
            {
              /*
                Planar configuration.
              */
              if (datum == 1)
                image->interlace=PlaneInterlace;
              break;
            }
            case 0x0008:
            {
              /*
                Number of frames.
              */
              if (data == (unsigned char *) NULL)
                break;
              number_scenes=StringToUnsignedLong((char *) data);
              break;
            }
            case 0x0010:
            {
              /*
                Image rows.
              */
              info.height=(size_t) datum;
              break;
            }
            case 0x0011:
            {
              /*
                Image columns.
              */
              info.width=(size_t) datum;
              break;
            }
            case 0x0100:
            {
              /*
                Bits allocated.
              */
              info.bits_allocated=(size_t) datum;
              info.bytes_per_pixel=1;
              if (datum > 8)
                info.bytes_per_pixel=2;
              info.depth=info.bits_allocated;
              if ((info.depth == 0) || (info.depth > 32))
                ThrowDCMException(CorruptImageError,"ImproperImageHeader")
              info.max_value=(1UL << info.bits_allocated)-1;
              image->depth=info.depth;
              break;
            }
            case 0x0101:
            {
              /*
                Bits stored.
              */
              info.significant_bits=(size_t) datum;
              info.bytes_per_pixel=1;
              if (info.significant_bits > 8)
                info.bytes_per_pixel=2;
              info.depth=info.significant_bits;
              if ((info.depth == 0) || (info.depth > 16))
                ThrowDCMException(CorruptImageError,"ImproperImageHeader")
              info.max_value=(1UL << info.significant_bits)-1;
              info.mask=(size_t) GetQuantumRange(info.significant_bits);
              image->depth=info.depth;
              break;
            }
            case 0x0102:
            {
              /*
                High bit.
              */
              break;
            }
            case 0x0103:
            {
              /*
                Pixel representation.
              */
              info.signed_data=(size_t) datum;
              break;
            }
            case 0x1050:
            {
              /*
                Visible pixel range: center.
              */
              if (data != (unsigned char *) NULL)
                info.window_center=StringToDouble((char *) data,(char **) NULL);
              break;
            }
            case 0x1051:
            {
              /*
                Visible pixel range: width.
              */
              if (data != (unsigned char *) NULL)
                info.window_width=StringToDouble((char *) data,(char **) NULL);
              break;
            }
            case 0x1052:
            {
              /*
                Rescale intercept
              */
              if (data != (unsigned char *) NULL)
                info.rescale_intercept=StringToDouble((char *) data,
                  (char **) NULL);
              break;
            }
            case 0x1053:
            {
              /*
                Rescale slope
              */
              if (data != (unsigned char *) NULL)
                info.rescale_slope=StringToDouble((char *) data,(char **) NULL);
              break;
            }
            case 0x1200:
            case 0x3006:
            {
              /*
                Populate graymap.
              */
              if (data == (unsigned char *) NULL)
                break;
              colors=(size_t) (length/info.bytes_per_pixel);
              datum=(int) colors;
              if (map.gray != (int *) NULL)
                map.gray=(int *) RelinquishMagickMemory(map.gray);
              map.gray=(int *) AcquireQuantumMemory(MagickMax(colors,65536),
                sizeof(*map.gray));
              if (map.gray == (int *) NULL)
                ThrowDCMException(ResourceLimitError,"MemoryAllocationFailed")
              (void) memset(map.gray,0,MagickMax(colors,65536)*
                sizeof(*map.gray));
              for (i=0; i < (ssize_t) colors; i++)
                if (info.bytes_per_pixel == 1)
                  map.gray[i]=(int) data[i];
                else
                  map.gray[i]=(int) ((short *) data)[i];
              break;
            }
            case 0x1201:
            {
              unsigned short
                index;

              /*
                Populate redmap.
              */
              if (data == (unsigned char *) NULL)
                break;
              colors=(size_t) (length/info.bytes_per_pixel);
              datum=(int) colors;
              if (map.red != (int *) NULL)
                map.red=(int *) RelinquishMagickMemory(map.red);
              map.red=(int *) AcquireQuantumMemory(MagickMax(colors,65536),
                sizeof(*map.red));
              if (map.red == (int *) NULL)
                ThrowDCMException(ResourceLimitError,"MemoryAllocationFailed")
              (void) memset(map.red,0,MagickMax(colors,65536)*
                sizeof(*map.red));
              p=data;
              for (i=0; i < (ssize_t) colors; i++)
              {
                if (image->endian == MSBEndian)
                  index=(unsigned short) ((*p << 8) | *(p+1));
                else
                  index=(unsigned short) (*p | (*(p+1) << 8));
                map.red[i]=(int) index;
                p+=2;
              }
              break;
            }
            case 0x1202:
            {
              unsigned short
                index;

              /*
                Populate greenmap.
              */
              if (data == (unsigned char *) NULL)
                break;
              colors=(size_t) (length/info.bytes_per_pixel);
              datum=(int) colors;
              if (map.green != (int *) NULL)
                map.green=(int *) RelinquishMagickMemory(map.green);
              map.green=(int *) AcquireQuantumMemory(MagickMax(colors,65536),
                sizeof(*map.green));
              if (map.green == (int *) NULL)
                ThrowDCMException(ResourceLimitError,"MemoryAllocationFailed")
              (void) memset(map.green,0,MagickMax(colors,65536)*
                sizeof(*map.green));
              p=data;
              for (i=0; i < (ssize_t) colors; i++)
              {
                if (image->endian == MSBEndian)
                  index=(unsigned short) ((*p << 8) | *(p+1));
                else
                  index=(unsigned short) (*p | (*(p+1) << 8));
                map.green[i]=(int) index;
                p+=2;
              }
              break;
            }
            case 0x1203:
            {
              unsigned short
                index;

              /*
                Populate bluemap.
              */
              if (data == (unsigned char *) NULL)
                break;
              colors=(size_t) (length/info.bytes_per_pixel);
              datum=(int) colors;
              if (map.blue != (int *) NULL)
                map.blue=(int *) RelinquishMagickMemory(map.blue);
              map.blue=(int *) AcquireQuantumMemory(MagickMax(colors,65536),
                sizeof(*map.blue));
              if (map.blue == (int *) NULL)
                ThrowDCMException(ResourceLimitError,"MemoryAllocationFailed")
              (void) memset(map.blue,0,MagickMax(colors,65536)*
                sizeof(*map.blue));
              p=data;
              for (i=0; i < (ssize_t) colors; i++)
              {
                if (image->endian == MSBEndian)
                  index=(unsigned short) ((*p << 8) | *(p+1));
                else
                  index=(unsigned short) (*p | (*(p+1) << 8));
                map.blue[i]=(int) index;
                p+=2;
              }
              break;
            }
            default:
              break;
          }
          break;
        }
        case 0x2050:
        {
          switch (element)
          {
            case 0x0020:
            {
              if ((data != (unsigned char *) NULL) &&
                  (strncmp((char *) data,"INVERSE",7) == 0))
                info.polarity=MagickTrue;
              break;
            }
            default:
              break;
          }
          break;
        }
        default:
          break;
      }
      if (data != (unsigned char *) NULL)
        {
          char
            *attribute;

          for (i=0; dicom_info[i].description != (char *) NULL; i++)
            if ((group == dicom_info[i].group) &&
                (element == dicom_info[i].element))
              break;
          if (dicom_info[i].description != (char *) NULL)
            {
              attribute=AcquireString("dcm:");
              (void) ConcatenateString(&attribute,dicom_info[i].description);
              for (i=0; i < (ssize_t) MagickMax(length,4); i++)
                if (isprint((int) data[i]) == 0)
                  break;
              if ((i == (ssize_t) length) || (length > 4))
                {
                  (void) SubstituteString(&attribute," ","");
                  (void) SetImageProperty(image,attribute,(char *) data,
                    exception);
                }
              attribute=DestroyString(attribute);
            }
        }
      if (image_info->verbose != MagickFalse)
        {
          if (data == (unsigned char *) NULL)
            (void) FormatLocaleFile(stdout,"%d\n",datum);
          else
            {
              /*
                Display group data.
              */
              for (i=0; i < (ssize_t) MagickMax(length,4); i++)
                if (isprint((int) data[i]) == 0)
                  break;
              if ((i != (ssize_t) length) && (length <= 4))
                {
                  ssize_t
                    j;

                  datum=0;
                  for (j=(ssize_t) length-1; j >= 0; j--)
                    datum=(256*datum+data[j]);
                  (void) FormatLocaleFile(stdout,"%d",datum);
                }
              else
                for (i=0; i < (ssize_t) length; i++)
                  if (isprint((int) data[i]) != 0)
                    (void) FormatLocaleFile(stdout,"%c",data[i]);
                  else
                    (void) FormatLocaleFile(stdout,"%c",'.');
              (void) FormatLocaleFile(stdout,"\n");
            }
        }
      if (data != (unsigned char *) NULL)
        data=(unsigned char *) RelinquishMagickMemory(data);
      if (EOFBlob(image) != MagickFalse)
        {
          ThrowFileException(exception,CorruptImageError,"UnexpectedEndOfFile",
            image->filename);
          group=0xfffc;
          break;
        }
    }
    if ((group == 0xfffc) && (element == 0xfffc))
      {
        Image
          *last;

        last=RemoveLastImageFromList(&image);
        if (last != (Image *) NULL)
          last=DestroyImage(last);
        break;
      }
    if ((info.width == 0) || (info.height == 0))
      ThrowDCMException(CorruptImageError,"ImproperImageHeader")
    image->columns=info.width;
    image->rows=info.height;
    if (info.signed_data == 0xffff)
      info.signed_data=(size_t) (info.significant_bits == 16 ? 1 : 0);
    if ((image->compression == JPEGCompression) ||
        (image->compression == JPEG2000Compression))
      {
        Image
          *images;

        ImageInfo
          *read_info;

        int
          c;

        /*
          Read offset table.
        */
        for (i=0; i < (ssize_t) stream_info->remaining; i++)
          if (ReadBlobByte(image) == EOF)
            break;
        (void) (((ssize_t) ReadBlobLSBShort(image) << 16) |
          ReadBlobLSBShort(image));
        length=(size_t) ReadBlobLSBLong(image);
        if (length > (size_t) GetBlobSize(image))
          ThrowDCMException(CorruptImageError,"InsufficientImageDataInFile")
        stream_info->offset_count=length >> 2;
        if (stream_info->offset_count != 0)
          {
            if (stream_info->offsets != (ssize_t *) NULL)
              stream_info->offsets=(ssize_t *) RelinquishMagickMemory(
                stream_info->offsets);
            stream_info->offsets=(ssize_t *) AcquireQuantumMemory(
              stream_info->offset_count,sizeof(*stream_info->offsets));
            if (stream_info->offsets == (ssize_t *) NULL)
              ThrowDCMException(ResourceLimitError,"MemoryAllocationFailed")
            for (i=0; i < (ssize_t) stream_info->offset_count; i++)
              stream_info->offsets[i]=(ssize_t) ReadBlobLSBSignedLong(image);
            offset=TellBlob(image);
            for (i=0; i < (ssize_t) stream_info->offset_count; i++)
              stream_info->offsets[i]+=offset;
          }
        /*
          Handle non-native image formats.
        */
        read_info=CloneImageInfo(image_info);
        SetImageInfoBlob(read_info,(void *) NULL,0);
        images=NewImageList();
        for (scene=0; scene < (ssize_t) number_scenes; scene++)
        {
          char
            filename[MagickPathExtent];

          const char
            *property;

          FILE
            *file;

          Image
            *jpeg_image;

          int
            unique_file;

          unsigned int
            tag;

          tag=((unsigned int) ReadBlobLSBShort(image) << 16) |
            ReadBlobLSBShort(image);
          length=(size_t) ReadBlobLSBLong(image);
          if (length > (size_t) GetBlobSize(image))
            {
              images=DestroyImageList(images);
              read_info=DestroyImageInfo(read_info);
              ThrowDCMException(CorruptImageError,"InsufficientImageDataInFile")
            }
          if (EOFBlob(image) != MagickFalse)
            {
              status=MagickFalse;
              break;
            }
          if (tag == 0xFFFEE0DD)
            break; /* sequence delimiter tag */
          if (tag != 0xFFFEE000)
            {
              status=MagickFalse;
              break;
            }
          file=(FILE *) NULL;
          unique_file=AcquireUniqueFileResource(filename);
          if (unique_file != -1)
            file=fdopen(unique_file,"wb");
          if (file == (FILE *) NULL)
            {
              (void) RelinquishUniqueFileResource(filename);
              ThrowFileException(exception,FileOpenError,
                "UnableToCreateTemporaryFile",filename);
              break;
            }
          for (c=EOF; length != 0; length--)
          {
            c=ReadBlobByte(image);
            if (c == EOF)
              {
                ThrowFileException(exception,CorruptImageError,
                  "UnexpectedEndOfFile",image->filename);
                break;
              }
            if (fputc(c,file) != c)
              break;
          }
          (void) fclose(file);
          if (c == EOF)
            break;
          (void) FormatLocaleString(read_info->filename,MagickPathExtent,
            "jpeg:%s",filename);
          if (image->compression == JPEG2000Compression)
            (void) FormatLocaleString(read_info->filename,MagickPathExtent,
              "j2k:%s",filename);
          jpeg_image=ReadImage(read_info,exception);
          if (jpeg_image != (Image *) NULL)
            {
              ResetImagePropertyIterator(image);
              property=GetNextImageProperty(image);
              while (property != (const char *) NULL)
              {
                (void) SetImageProperty(jpeg_image,property,
                  GetImageProperty(image,property,exception),exception);
                property=GetNextImageProperty(image);
              }
              AppendImageToList(&images,jpeg_image);
            }
          (void) RelinquishUniqueFileResource(filename);
        }
        read_info=DestroyImageInfo(read_info);
        image=DestroyImageList(image);
        if ((status == MagickFalse) && (exception->severity < ErrorException))
          {
            images=DestroyImageList(images);
            ThrowDCMException(CorruptImageError,"CorruptImageError")
          }
        else
          RelinquishDCMMemory(&info,&map,stream_info,stack,data);
        return(GetFirstImageInList(images));
      }
    if (info.depth != (1UL*MAGICKCORE_QUANTUM_DEPTH))
      {
        QuantumAny
          range;

        /*
          Compute pixel scaling table.
        */
        length=(size_t) (GetQuantumRange(info.depth)+1);
        if (length > (size_t) GetBlobSize(image))
          ThrowDCMException(CorruptImageError,"InsufficientImageDataInFile")
        if (info.scale != (Quantum *) NULL)
          info.scale=(Quantum *) RelinquishMagickMemory(info.scale);
        info.scale_size=MagickMax(length,MaxMap)+1;
        info.scale=(Quantum *) AcquireQuantumMemory(info.scale_size,
          sizeof(*info.scale));
        if (info.scale == (Quantum *) NULL)
          ThrowDCMException(ResourceLimitError,"MemoryAllocationFailed")
        (void) memset(info.scale,0,(MagickMax(length,MaxMap)+1)*
          sizeof(*info.scale));
        range=GetQuantumRange(info.depth);
        for (i=0; i <= (ssize_t) GetQuantumRange(info.depth); i++)
          info.scale[i]=ScaleAnyToQuantum((size_t) i,range);
      }
    if (image->compression == RLECompression)
      {
        unsigned int
          tag;

        /*
          Read RLE offset table.
        */
        for (i=0; i < (ssize_t) stream_info->remaining; i++)
        {
          int
            c;

          c=ReadBlobByte(image);
          if (c == EOF)
            break;
        }
        tag=((unsigned int) ReadBlobLSBShort(image) << 16) |
          ReadBlobLSBShort(image);
        (void) tag;
        length=(size_t) ReadBlobLSBLong(image);
        if (length > (size_t) GetBlobSize(image))
          ThrowDCMException(CorruptImageError,"InsufficientImageDataInFile")
        stream_info->offset_count=length >> 2;
        if (stream_info->offset_count != 0)
          {
            if (stream_info->offsets != (ssize_t *) NULL)
              stream_info->offsets=(ssize_t *)
                RelinquishMagickMemory(stream_info->offsets);
            stream_info->offsets=(ssize_t *) AcquireQuantumMemory(
              stream_info->offset_count,sizeof(*stream_info->offsets));
            if (stream_info->offsets == (ssize_t *) NULL)
              ThrowDCMException(ResourceLimitError,"MemoryAllocationFailed")
            for (i=0; i < (ssize_t) stream_info->offset_count; i++)
            {
              offset=(MagickOffsetType) ReadBlobLSBSignedLong(image);
              if (offset > (MagickOffsetType) GetBlobSize(image))
                ThrowDCMException(CorruptImageError,
                  "InsufficientImageDataInFile")
              stream_info->offsets[i]=(ssize_t) offset;
              if (EOFBlob(image) != MagickFalse)
                break;
            }
            offset=TellBlob(image)+8;
            for (i=0; i < (ssize_t) stream_info->offset_count; i++)
              stream_info->offsets[i]+=offset;
          }
      }
    for (scene=0; scene < (ssize_t) number_scenes; scene++)
    {
      image->columns=info.width;
      image->rows=info.height;
      image->depth=info.depth;
      status=SetImageExtent(image,image->columns,image->rows,exception);
      if (status == MagickFalse)
        break;
      image->colorspace=RGBColorspace;
      (void) SetImageBackgroundColor(image,exception);
      if ((image->colormap == (PixelInfo *) NULL) &&
          (info.samples_per_pixel == 1))
        {
          int
            index;

          size_t
            one;

          one=1;
          if (colors == 0)
            colors=one << info.depth;
          if (AcquireImageColormap(image,colors,exception) == MagickFalse)
            ThrowDCMException(ResourceLimitError,"MemoryAllocationFailed")
          if (map.red != (int *) NULL)
            for (i=0; i < (ssize_t) colors; i++)
            {
              index=map.red[i];
              if ((info.scale != (Quantum *) NULL) && (index >= 0) &&
                  (index <= (int) info.max_value))
                index=(int) info.scale[index];
              image->colormap[i].red=(MagickRealType) index;
            }
          if (map.green != (int *) NULL)
            for (i=0; i < (ssize_t) colors; i++)
            {
              index=map.green[i];
              if ((info.scale != (Quantum *) NULL) && (index >= 0) &&
                  (index <= (int) info.max_value))
                index=(int) info.scale[index];
              image->colormap[i].green=(MagickRealType) index;
            }
          if (map.blue != (int *) NULL)
            for (i=0; i < (ssize_t) colors; i++)
            {
              index=map.blue[i];
              if ((info.scale != (Quantum *) NULL) && (index >= 0) &&
                  (index <= (int) info.max_value))
                index=(int) info.scale[index];
              image->colormap[i].blue=(MagickRealType) index;
            }
          if (map.gray != (int *) NULL)
            for (i=0; i < (ssize_t) colors; i++)
            {
              index=map.gray[i];
              if ((info.scale != (Quantum *) NULL) && (index >= 0) &&
                  (index <= (int) info.max_value))
                index=(int) info.scale[index];
              image->colormap[i].red=(MagickRealType) index;
              image->colormap[i].green=(MagickRealType) index;
              image->colormap[i].blue=(MagickRealType) index;
            }
        }
      if (image->compression == RLECompression)
        {
          unsigned int
            tag;

          /*
            Read RLE segment table.
          */
          for (i=0; i < (ssize_t) stream_info->remaining; i++)
          {
            int
              c;

            c=ReadBlobByte(image);
            if (c == EOF)
              break;
          }
          tag=((unsigned int) ReadBlobLSBShort(image) << 16) |
            ReadBlobLSBShort(image);
          stream_info->remaining=(size_t) ReadBlobLSBLong(image);
          if ((tag != 0xFFFEE000) || (stream_info->remaining <= 64) ||
              (EOFBlob(image) != MagickFalse))
            {
              if (stream_info->offsets != (ssize_t *) NULL)
                stream_info->offsets=(ssize_t *)
                  RelinquishMagickMemory(stream_info->offsets);
              ThrowDCMException(CorruptImageError,"ImproperImageHeader")
            }
          stream_info->count=0;
          stream_info->segment_count=ReadBlobLSBLong(image);
          for (i=0; i < 15; i++)
            stream_info->segments[i]=(ssize_t) ReadBlobLSBSignedLong(image);
          stream_info->remaining-=64;
          if (stream_info->segment_count > 1)
            {
              info.bytes_per_pixel=1;
              info.depth=8;
              if (stream_info->offset_count > 0)
                (void) SeekBlob(image,(MagickOffsetType)
                  stream_info->offsets[0]+stream_info->segments[0],SEEK_SET);
            }
        }
      if ((info.samples_per_pixel > 1) && (image->interlace == PlaneInterlace))
        {
          Quantum
            *q;

          ssize_t
            x,
            y;

          /*
            Convert Planar RGB DCM Medical image to pixel packets.
          */
          for (i=0; i < (ssize_t) info.samples_per_pixel; i++)
          {
            for (y=0; y < (ssize_t) image->rows; y++)
            {
              q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
              if (q == (Quantum *) NULL)
                break;
              for (x=0; x < (ssize_t) image->columns; x++)
              {
                switch ((int) i)
                {
                  case 0:
                  {
                    SetPixelRed(image,ScaleCharToQuantum((unsigned char)
                      ReadDCMByte(stream_info,image)),q);
                    break;
                  }
                  case 1:
                  {
                    SetPixelGreen(image,ScaleCharToQuantum((unsigned char)
                      ReadDCMByte(stream_info,image)),q);
                    break;
                  }
                  case 2:
                  {
                    SetPixelBlue(image,ScaleCharToQuantum((unsigned char)
                      ReadDCMByte(stream_info,image)),q);
                    break;
                  }
                  case 3:
                  {
                    SetPixelAlpha(image,ScaleCharToQuantum((unsigned char)
                      ReadDCMByte(stream_info,image)),q);
                    break;
                  }
                  default:
                    break;
                }
                q+=GetPixelChannels(image);
              }
              if (SyncAuthenticPixels(image,exception) == MagickFalse)
                break;
              if (image->previous == (Image *) NULL)
                {
                  status=SetImageProgress(image,LoadImageTag,(MagickOffsetType)
                    y,image->rows);
                  if (status == MagickFalse)
                    break;
                }
            }
          }
        }
      else
        {
          const char
            *option;

          /*
            Convert DCM Medical image to pixel packets.
          */
          option=GetImageOption(image_info,"dcm:display-range");
          if (option != (const char *) NULL)
            {
              if (LocaleCompare(option,"reset") == 0)
                info.window_width=0;
            }
          option=GetImageOption(image_info,"dcm:window");
          if (option != (char *) NULL)
            {
              GeometryInfo
                geometry_info;

              MagickStatusType
                flags;

              flags=ParseGeometry(option,&geometry_info);
              if (flags & RhoValue)
                info.window_center=geometry_info.rho;
              if (flags & SigmaValue)
                info.window_width=geometry_info.sigma;
              info.rescale=MagickTrue;
            }
          option=GetImageOption(image_info,"dcm:rescale");
          if (option != (char *) NULL)
            info.rescale=IsStringTrue(option);
          if ((info.window_center != 0) && (info.window_width == 0))
            info.window_width=info.window_center;
          status=ReadDCMPixels(image,&info,stream_info,MagickTrue,exception);
          if ((status != MagickFalse) && (stream_info->segment_count > 1))
            {
              if (stream_info->offset_count > 0)
                (void) SeekBlob(image,(MagickOffsetType)
                  stream_info->offsets[0]+stream_info->segments[1],SEEK_SET);
              (void) ReadDCMPixels(image,&info,stream_info,MagickFalse,
                exception);
            }
        }
      if (IdentifyImageCoderGray(image,exception) != MagickFalse)
        (void) SetImageColorspace(image,GRAYColorspace,exception);
      if (EOFBlob(image) != MagickFalse)
        {
          ThrowFileException(exception,CorruptImageError,"UnexpectedEndOfFile",
            image->filename);
          break;
        }
      /*
        Proceed to next image.
      */
      if (image_info->number_scenes != 0)
        if (image->scene >= (image_info->scene+image_info->number_scenes-1))
          break;
      if (scene < (ssize_t) (number_scenes-1))
        {
          /*
            Allocate next image structure.
          */
          AcquireNextImage(image_info,image,exception);
          if (GetNextImageInList(image) == (Image *) NULL)
            {
              status=MagickFalse;
              break;
            }
          image=SyncNextImageInList(image);
          status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
            GetBlobSize(image));
          if (status == MagickFalse)
            break;
        }
    }
    if (TellBlob(image) < (MagickOffsetType) GetBlobSize(image))
      {
        /*
          Allocate next image structure.
        */
        AcquireNextImage(image_info,image,exception);
        if (GetNextImageInList(image) == (Image *) NULL)
          {
            status=MagickFalse;
            break;
          }
        image=SyncNextImageInList(image);
        status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
          GetBlobSize(image));
        if (status == MagickFalse)
          break;
     }
  }
  /*
    Free resources.
  */
  RelinquishDCMMemory(&info,&map,stream_info,stack,data);
  if (image == (Image *) NULL)
    return(image);
  (void) CloseBlob(image);
  if (status == MagickFalse)
    return(DestroyImageList(image));
  return(GetFirstImageInList(image));
}


static void *RelinquishDCMInfo(void *memory)
{
  DCMInfo
    *info;

  info=(DCMInfo *) memory;
  if (info->scale != (Quantum *) NULL)
    info->scale=(Quantum *) RelinquishMagickMemory(info->scale);
  return(RelinquishMagickMemory(info));
}

Coders: https://github.com/ImageMagick/ImageMagick/issues/4947


static unsigned short ReadDCMShort(DCMStreamInfo *stream_info,Image *image)
{
  int
    shift,
    byte;

  unsigned short
    value;

  if (image->compression != RLECompression)
    return(ReadBlobLSBShort(image));
  shift=image->depth < 16 ? 4 : 8;
  value=(unsigned short) ReadDCMByte(stream_info,image);
  byte=ReadDCMByte(stream_info,image);
  if (byte < 0)
    return(0);
  value|=(unsigned short) (byte << shift);
  return(value);
}


static MagickBooleanType IsDCM(const unsigned char *magick,const size_t length)
{
  if (length < 132)
    return(MagickFalse);
  if (LocaleNCompare((char *) (magick+128),"DICM",4) == 0)
    return(MagickTrue);
  return(MagickFalse);
}


static signed short ReadDCMSignedShort(DCMStreamInfo *stream_info,Image *image)
{
  union
  {
    unsigned short
      unsigned_value;

    signed short
      signed_value;
  } quantum;

  quantum.unsigned_value=ReadDCMShort(stream_info,image);
  return(quantum.signed_value);
}


inline void BinaryBroadcastFiveFold(const ArithmeticParams& unswitched_params,
                                    const RuntimeShape& unswitched_input1_shape,
                                    const T* unswitched_input1_data,
                                    const RuntimeShape& unswitched_input2_shape,
                                    const T* unswitched_input2_data,
                                    const RuntimeShape& output_shape,
                                    T* output_data, ElementwiseF elementwise_f,
                                    ScalarBroadcastF scalar_broadcast_f) {
  ArithmeticParams switched_params = unswitched_params;
  switched_params.input1_offset = unswitched_params.input2_offset;
  switched_params.input1_multiplier = unswitched_params.input2_multiplier;
  switched_params.input1_shift = unswitched_params.input2_shift;
  switched_params.input2_offset = unswitched_params.input1_offset;
  switched_params.input2_multiplier = unswitched_params.input1_multiplier;
  switched_params.input2_shift = unswitched_params.input1_shift;

  const bool use_unswitched =
      unswitched_params.broadcast_category ==
      tflite::BroadcastableOpCategory::kFirstInputBroadcastsFast;

  const ArithmeticParams& params =
      use_unswitched ? unswitched_params : switched_params;
  const T* input1_data =
      use_unswitched ? unswitched_input1_data : unswitched_input2_data;
  const T* input2_data =
      use_unswitched ? unswitched_input2_data : unswitched_input1_data;

  // Fivefold nested loops. The second input resets its position for each
  // iteration of the second loop. The first input resets its position at the
  // beginning of the fourth loop. The innermost loop is an elementwise add of
  // sections of the arrays.
  T* output_data_ptr = output_data;
  const T* input1_data_ptr = input1_data;
  const T* input2_data_reset = input2_data;
  // In the fivefold pattern, y0, y2 and y4 are not broadcast, and so shared
  // between input shapes. y3 for input 1 is always broadcast, and so the
  // dimension there is 1, whereas optionally y1 might be broadcast for
  // input 2. Put another way, input1.shape.FlatSize = y0 * y1 * y2 * y4,
  // input2.shape.FlatSize = y0 * y2 * y3 * y4.
  int y0 = params.broadcast_shape[0];
  int y1 = params.broadcast_shape[1];
  int y2 = params.broadcast_shape[2];
  int y3 = params.broadcast_shape[3];
  int y4 = params.broadcast_shape[4];
  if (y4 > 1) {
    // General fivefold pattern, with y4 > 1 so there is a non-broadcast inner
    // dimension.
    for (int i0 = 0; i0 < y0; ++i0) {
      const T* input2_data_ptr = nullptr;
      for (int i1 = 0; i1 < y1; ++i1) {
        input2_data_ptr = input2_data_reset;
        for (int i2 = 0; i2 < y2; ++i2) {
          for (int i3 = 0; i3 < y3; ++i3) {
            elementwise_f(y4, params, input1_data_ptr, input2_data_ptr,
                          output_data_ptr);
            input2_data_ptr += y4;
            output_data_ptr += y4;
          }
          // We have broadcast y4 of input1 data y3 times, and now move on.
          input1_data_ptr += y4;
        }
      }
      // We have broadcast y2*y3*y4 of input2 data y1 times, and now move on.
      input2_data_reset = input2_data_ptr;
    }
  } else if (input1_data_ptr != nullptr) {
    // Special case of y4 == 1, in which the innermost loop is a single
    // element and can be combined with the next (y3) as an inner broadcast.
    //
    // Note that this handles the case of pure scalar broadcast when
    // y0 == y1 == y2 == 1. With low overhead it handles cases such as scalar
    // broadcast with batch (as y2 > 1).
    //
    // NOTE The process is the same as the above general case except
    // simplified for y4 == 1 and the loop over y3 is contained within the
    // AddScalarBroadcast function.
    for (int i0 = 0; i0 < y0; ++i0) {
      const T* input2_data_ptr = nullptr;
      for (int i1 = 0; i1 < y1; ++i1) {
        input2_data_ptr = input2_data_reset;
        for (int i2 = 0; i2 < y2; ++i2) {
          scalar_broadcast_f(y3, params, *input1_data_ptr, input2_data_ptr,
                             output_data_ptr);
          input2_data_ptr += y3;
          output_data_ptr += y3;
          input1_data_ptr += 1;
        }
      }
      input2_data_reset = input2_data_ptr;
    }
  }
}

Prevent dereferencing of null pointers in TFLite's `add.cc`.

PiperOrigin-RevId: 387244946
Change-Id: I56094233327fbd8439b92e1dbb1262176e00eeb9


static unsigned short reverseLutFromBitmap(
    const unsigned char bitmap[BITMAP_SIZE], unsigned short lut[USHORT_RANGE]) {
  int k = 0;

  for (int i = 0; i < USHORT_RANGE; ++i) {
    if ((i == 0) || (bitmap[i >> 3] & (1 << (i & 7)))) lut[k++] = i;
  }

  int n = k - 1;

  while (k < USHORT_RANGE) lut[k++] = 0;

  return n;  // maximum k where lut[k] is non-zero,
}  // i.e. number of ones in bitmap minus 1

Make line_no with too large value(2**20) invalid. Fixes #124


static void cpy2(unsigned short *dst_val, const unsigned short *src_val) {
  unsigned char *dst = reinterpret_cast<unsigned char *>(dst_val);
  const unsigned char *src = reinterpret_cast<const unsigned char *>(src_val);

  dst[0] = src[0];
  dst[1] = src[1];
}


inline long long getBits(int nBits, long long &c, int &lc, const char *&in) {
  while (lc < nBits) {
    c = (c << 8) | *(reinterpret_cast<const unsigned char *>(in++));
    lc += 8;
  }

  lc -= nBits;
  return (c >> lc) & ((1 << nBits) - 1);
}


mz_bool mz_zip_reader_init_file(mz_zip_archive *pZip, const char *pFilename,
                                mz_uint32 flags) {
  mz_uint64 file_size;
  MZ_FILE *pFile = MZ_FOPEN(pFilename, "rb");
  if (!pFile) return MZ_FALSE;
  if (MZ_FSEEK64(pFile, 0, SEEK_END)) {
    MZ_FCLOSE(pFile);
    return MZ_FALSE;
  }
  file_size = MZ_FTELL64(pFile);
  if (!mz_zip_reader_init_internal(pZip, flags)) {
    MZ_FCLOSE(pFile);
    return MZ_FALSE;
  }
  pZip->m_pRead = mz_zip_file_read_func;
  pZip->m_pIO_opaque = pZip;
  pZip->m_pState->m_pFile = pFile;
  pZip->m_archive_size = file_size;
  if (!mz_zip_reader_read_central_dir(pZip, flags)) {
    mz_zip_reader_end(pZip);
    return MZ_FALSE;
  }
  return MZ_TRUE;
}


mz_bool mz_zip_writer_end(mz_zip_archive *pZip) {
  mz_zip_internal_state *pState;
  mz_bool status = MZ_TRUE;
  if ((!pZip) || (!pZip->m_pState) || (!pZip->m_pAlloc) || (!pZip->m_pFree) ||
      ((pZip->m_zip_mode != MZ_ZIP_MODE_WRITING) &&
       (pZip->m_zip_mode != MZ_ZIP_MODE_WRITING_HAS_BEEN_FINALIZED)))
    return MZ_FALSE;

  pState = pZip->m_pState;
  pZip->m_pState = NULL;
  mz_zip_array_clear(pZip, &pState->m_central_dir);
  mz_zip_array_clear(pZip, &pState->m_central_dir_offsets);
  mz_zip_array_clear(pZip, &pState->m_sorted_central_dir_offsets);

#ifndef MINIZ_NO_STDIO
  if (pState->m_pFile) {
    MZ_FCLOSE(pState->m_pFile);
    pState->m_pFile = NULL;
  }
#endif  // #ifndef MINIZ_NO_STDIO

  if ((pZip->m_pWrite == mz_zip_heap_write_func) && (pState->m_pMem)) {
    pZip->m_pFree(pZip->m_pAlloc_opaque, pState->m_pMem);
    pState->m_pMem = NULL;
  }

  pZip->m_pFree(pZip->m_pAlloc_opaque, pState);
  pZip->m_zip_mode = MZ_ZIP_MODE_INVALID;
  return status;
}


static void SetErrorMessage(const std::string &msg, const char **err) {
  if (err) {
#ifdef _WIN32
    (*err) = _strdup(msg.c_str());
#else
    (*err) = strdup(msg.c_str());
#endif
  }
}


static void tdefl_huffman_enforce_max_code_size(int *pNum_codes,
                                                int code_list_len,
                                                int max_code_size) {
  int i;
  mz_uint32 total = 0;
  if (code_list_len <= 1) return;
  for (i = max_code_size + 1; i <= TDEFL_MAX_SUPPORTED_HUFF_CODESIZE; i++)
    pNum_codes[max_code_size] += pNum_codes[i];
  for (i = max_code_size; i > 0; i--)
    total += (((mz_uint32)pNum_codes[i]) << (max_code_size - i));
  while (total != (1UL << max_code_size)) {
    pNum_codes[max_code_size]--;
    for (i = max_code_size - 1; i > 0; i--)
      if (pNum_codes[i]) {
        pNum_codes[i]--;
        pNum_codes[i + 1] += 2;
        break;
      }
    total--;
  }
}


int mz_uncompress(unsigned char *pDest, mz_ulong *pDest_len,
                  const unsigned char *pSource, mz_ulong source_len) {
  mz_stream stream;
  int status;
  memset(&stream, 0, sizeof(stream));

  // In case mz_ulong is 64-bits (argh I hate longs).
  if ((source_len | *pDest_len) > 0xFFFFFFFFU) return MZ_PARAM_ERROR;

  stream.next_in = pSource;
  stream.avail_in = (mz_uint32)source_len;
  stream.next_out = pDest;
  stream.avail_out = (mz_uint32)*pDest_len;

  status = mz_inflateInit(&stream);
  if (status != MZ_OK) return status;

  status = mz_inflate(&stream, MZ_FINISH);
  if (status != MZ_STREAM_END) {
    mz_inflateEnd(&stream);
    return ((status == MZ_BUF_ERROR) && (!stream.avail_in)) ? MZ_DATA_ERROR
                                                            : status;
  }
  *pDest_len = stream.total_out;

  return mz_inflateEnd(&stream);
}


static mz_bool tdefl_compress_fast(tdefl_compressor *d) {
  // Faster, minimally featured LZRW1-style match+parse loop with better
  // register utilization. Intended for applications where raw throughput is
  // valued more highly than ratio.
  mz_uint lookahead_pos = d->m_lookahead_pos,
          lookahead_size = d->m_lookahead_size, dict_size = d->m_dict_size,
          total_lz_bytes = d->m_total_lz_bytes,
          num_flags_left = d->m_num_flags_left;
  mz_uint8 *pLZ_code_buf = d->m_pLZ_code_buf, *pLZ_flags = d->m_pLZ_flags;
  mz_uint cur_pos = lookahead_pos & TDEFL_LZ_DICT_SIZE_MASK;

  while ((d->m_src_buf_left) || ((d->m_flush) && (lookahead_size))) {
    const mz_uint TDEFL_COMP_FAST_LOOKAHEAD_SIZE = 4096;
    mz_uint dst_pos =
        (lookahead_pos + lookahead_size) & TDEFL_LZ_DICT_SIZE_MASK;
    mz_uint num_bytes_to_process = (mz_uint)MZ_MIN(
        d->m_src_buf_left, TDEFL_COMP_FAST_LOOKAHEAD_SIZE - lookahead_size);
    d->m_src_buf_left -= num_bytes_to_process;
    lookahead_size += num_bytes_to_process;

    while (num_bytes_to_process) {
      mz_uint32 n = MZ_MIN(TDEFL_LZ_DICT_SIZE - dst_pos, num_bytes_to_process);
      memcpy(d->m_dict + dst_pos, d->m_pSrc, n);
      if (dst_pos < (TDEFL_MAX_MATCH_LEN - 1))
        memcpy(d->m_dict + TDEFL_LZ_DICT_SIZE + dst_pos, d->m_pSrc,
               MZ_MIN(n, (TDEFL_MAX_MATCH_LEN - 1) - dst_pos));
      d->m_pSrc += n;
      dst_pos = (dst_pos + n) & TDEFL_LZ_DICT_SIZE_MASK;
      num_bytes_to_process -= n;
    }

    dict_size = MZ_MIN(TDEFL_LZ_DICT_SIZE - lookahead_size, dict_size);
    if ((!d->m_flush) && (lookahead_size < TDEFL_COMP_FAST_LOOKAHEAD_SIZE))
      break;

    while (lookahead_size >= 4) {
      mz_uint cur_match_dist, cur_match_len = 1;
      mz_uint8 *pCur_dict = d->m_dict + cur_pos;
      mz_uint first_trigram = (*(const mz_uint32 *)pCur_dict) & 0xFFFFFF;
      mz_uint hash =
          (first_trigram ^ (first_trigram >> (24 - (TDEFL_LZ_HASH_BITS - 8)))) &
          TDEFL_LEVEL1_HASH_SIZE_MASK;
      mz_uint probe_pos = d->m_hash[hash];
      d->m_hash[hash] = (mz_uint16)lookahead_pos;

      if (((cur_match_dist = (mz_uint16)(lookahead_pos - probe_pos)) <=
           dict_size) &&
          ((*(const mz_uint32 *)(d->m_dict +
                                 (probe_pos &= TDEFL_LZ_DICT_SIZE_MASK)) &
            0xFFFFFF) == first_trigram)) {
        const mz_uint16 *p = (const mz_uint16 *)pCur_dict;
        const mz_uint16 *q = (const mz_uint16 *)(d->m_dict + probe_pos);
        mz_uint32 probe_len = 32;
        do {
        } while ((TDEFL_READ_UNALIGNED_WORD(++p) ==
                  TDEFL_READ_UNALIGNED_WORD(++q)) &&
                 (TDEFL_READ_UNALIGNED_WORD(++p) ==
                  TDEFL_READ_UNALIGNED_WORD(++q)) &&
                 (TDEFL_READ_UNALIGNED_WORD(++p) ==
                  TDEFL_READ_UNALIGNED_WORD(++q)) &&
                 (TDEFL_READ_UNALIGNED_WORD(++p) ==
                  TDEFL_READ_UNALIGNED_WORD(++q)) &&
                 (--probe_len > 0));
        cur_match_len = ((mz_uint)(p - (const mz_uint16 *)pCur_dict) * 2) +
                        (mz_uint)(*(const mz_uint8 *)p == *(const mz_uint8 *)q);
        if (!probe_len)
          cur_match_len = cur_match_dist ? TDEFL_MAX_MATCH_LEN : 0;

        if ((cur_match_len < TDEFL_MIN_MATCH_LEN) ||
            ((cur_match_len == TDEFL_MIN_MATCH_LEN) &&
             (cur_match_dist >= 8U * 1024U))) {
          cur_match_len = 1;
          *pLZ_code_buf++ = (mz_uint8)first_trigram;
          *pLZ_flags = (mz_uint8)(*pLZ_flags >> 1);
          d->m_huff_count[0][(mz_uint8)first_trigram]++;
        } else {
          mz_uint32 s0, s1;
          cur_match_len = MZ_MIN(cur_match_len, lookahead_size);

          MZ_ASSERT((cur_match_len >= TDEFL_MIN_MATCH_LEN) &&
                    (cur_match_dist >= 1) &&
                    (cur_match_dist <= TDEFL_LZ_DICT_SIZE));

          cur_match_dist--;

          pLZ_code_buf[0] = (mz_uint8)(cur_match_len - TDEFL_MIN_MATCH_LEN);
          *(mz_uint16 *)(&pLZ_code_buf[1]) = (mz_uint16)cur_match_dist;
          pLZ_code_buf += 3;
          *pLZ_flags = (mz_uint8)((*pLZ_flags >> 1) | 0x80);

          s0 = s_tdefl_small_dist_sym[cur_match_dist & 511];
          s1 = s_tdefl_large_dist_sym[cur_match_dist >> 8];
          d->m_huff_count[1][(cur_match_dist < 512) ? s0 : s1]++;

          d->m_huff_count[0][s_tdefl_len_sym[cur_match_len -
                                             TDEFL_MIN_MATCH_LEN]]++;
        }
      } else {
        *pLZ_code_buf++ = (mz_uint8)first_trigram;
        *pLZ_flags = (mz_uint8)(*pLZ_flags >> 1);
        d->m_huff_count[0][(mz_uint8)first_trigram]++;
      }

      if (--num_flags_left == 0) {
        num_flags_left = 8;
        pLZ_flags = pLZ_code_buf++;
      }

      total_lz_bytes += cur_match_len;
      lookahead_pos += cur_match_len;
      dict_size = MZ_MIN(dict_size + cur_match_len, TDEFL_LZ_DICT_SIZE);
      cur_pos = (cur_pos + cur_match_len) & TDEFL_LZ_DICT_SIZE_MASK;
      MZ_ASSERT(lookahead_size >= cur_match_len);
      lookahead_size -= cur_match_len;

      if (pLZ_code_buf > &d->m_lz_code_buf[TDEFL_LZ_CODE_BUF_SIZE - 8]) {
        int n;
        d->m_lookahead_pos = lookahead_pos;
        d->m_lookahead_size = lookahead_size;
        d->m_dict_size = dict_size;
        d->m_total_lz_bytes = total_lz_bytes;
        d->m_pLZ_code_buf = pLZ_code_buf;
        d->m_pLZ_flags = pLZ_flags;
        d->m_num_flags_left = num_flags_left;
        if ((n = tdefl_flush_block(d, 0)) != 0)
          return (n < 0) ? MZ_FALSE : MZ_TRUE;
        total_lz_bytes = d->m_total_lz_bytes;
        pLZ_code_buf = d->m_pLZ_code_buf;
        pLZ_flags = d->m_pLZ_flags;
        num_flags_left = d->m_num_flags_left;
      }
    }

    while (lookahead_size) {
      mz_uint8 lit = d->m_dict[cur_pos];

      total_lz_bytes++;
      *pLZ_code_buf++ = lit;
      *pLZ_flags = (mz_uint8)(*pLZ_flags >> 1);
      if (--num_flags_left == 0) {
        num_flags_left = 8;
        pLZ_flags = pLZ_code_buf++;
      }

      d->m_huff_count[0][lit]++;

      lookahead_pos++;
      dict_size = MZ_MIN(dict_size + 1, TDEFL_LZ_DICT_SIZE);
      cur_pos = (cur_pos + 1) & TDEFL_LZ_DICT_SIZE_MASK;
      lookahead_size--;

      if (pLZ_code_buf > &d->m_lz_code_buf[TDEFL_LZ_CODE_BUF_SIZE - 8]) {
        int n;
        d->m_lookahead_pos = lookahead_pos;
        d->m_lookahead_size = lookahead_size;
        d->m_dict_size = dict_size;
        d->m_total_lz_bytes = total_lz_bytes;
        d->m_pLZ_code_buf = pLZ_code_buf;
        d->m_pLZ_flags = pLZ_flags;
        d->m_num_flags_left = num_flags_left;
        if ((n = tdefl_flush_block(d, 0)) != 0)
          return (n < 0) ? MZ_FALSE : MZ_TRUE;
        total_lz_bytes = d->m_total_lz_bytes;
        pLZ_code_buf = d->m_pLZ_code_buf;
        pLZ_flags = d->m_pLZ_flags;
        num_flags_left = d->m_num_flags_left;
      }
    }
  }

  d->m_lookahead_pos = lookahead_pos;
  d->m_lookahead_size = lookahead_size;
  d->m_dict_size = dict_size;
  d->m_total_lz_bytes = total_lz_bytes;
  d->m_pLZ_code_buf = pLZ_code_buf;
  d->m_pLZ_flags = pLZ_flags;
  d->m_num_flags_left = num_flags_left;
  return MZ_TRUE;
}


mz_bool mz_zip_add_mem_to_archive_file_in_place(
    const char *pZip_filename, const char *pArchive_name, const void *pBuf,
    size_t buf_size, const void *pComment, mz_uint16 comment_size,
    mz_uint level_and_flags) {
  mz_bool status, created_new_archive = MZ_FALSE;
  mz_zip_archive zip_archive;
  struct MZ_FILE_STAT_STRUCT file_stat;
  MZ_CLEAR_OBJ(zip_archive);
  if ((int)level_and_flags < 0) level_and_flags = MZ_DEFAULT_LEVEL;
  if ((!pZip_filename) || (!pArchive_name) || ((buf_size) && (!pBuf)) ||
      ((comment_size) && (!pComment)) ||
      ((level_and_flags & 0xF) > MZ_UBER_COMPRESSION))
    return MZ_FALSE;
  if (!mz_zip_writer_validate_archive_name(pArchive_name)) return MZ_FALSE;
  if (MZ_FILE_STAT(pZip_filename, &file_stat) != 0) {
    // Create a new archive.
    if (!mz_zip_writer_init_file(&zip_archive, pZip_filename, 0))
      return MZ_FALSE;
    created_new_archive = MZ_TRUE;
  } else {
    // Append to an existing archive.
    if (!mz_zip_reader_init_file(
            &zip_archive, pZip_filename,
            level_and_flags | MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY))
      return MZ_FALSE;
    if (!mz_zip_writer_init_from_reader(&zip_archive, pZip_filename)) {
      mz_zip_reader_end(&zip_archive);
      return MZ_FALSE;
    }
  }
  status =
      mz_zip_writer_add_mem_ex(&zip_archive, pArchive_name, pBuf, buf_size,
                               pComment, comment_size, level_and_flags, 0, 0);
  // Always finalize, even if adding failed for some reason, so we have a valid
  // central directory. (This may not always succeed, but we can try.)
  if (!mz_zip_writer_finalize_archive(&zip_archive)) status = MZ_FALSE;
  if (!mz_zip_writer_end(&zip_archive)) status = MZ_FALSE;
  if ((!status) && (created_new_archive)) {
    // It's a new archive and something went wrong, so just delete it.
    int ignoredStatus = MZ_DELETE_FILE(pZip_filename);
    (void)ignoredStatus;
  }
  return status;
}


static int hufEncode            // return: output size (in bits)
    (const long long *hcode,    // i : encoding table
     const unsigned short *in,  // i : uncompressed input buffer
     const int ni,              // i : input buffer size (in bytes)
     int rlc,                   // i : rl code
     char *out)                 //  o: compressed output buffer
{
  char *outStart = out;
  long long c = 0;  // bits not yet written to out
  int lc = 0;       // number of valid bits in c (LSB)
  int s = in[0];
  int cs = 0;

  //
  // Loop on input values
  //

  for (int i = 1; i < ni; i++) {
    //
    // Count same values or send code
    //

    if (s == in[i] && cs < 255) {
      cs++;
    } else {
      sendCode(hcode[s], cs, hcode[rlc], c, lc, out);
      cs = 0;
    }

    s = in[i];
  }

  //
  // Send remaining code
  //

  sendCode(hcode[s], cs, hcode[rlc], c, lc, out);

  if (lc) *out = (c << (8 - lc)) & 0xff;

  return (out - outStart) * 8 + lc;
}


static void WriteChannelInfo(std::vector<unsigned char> &data,
                             const std::vector<ChannelInfo> &channels) {
  size_t sz = 0;

  // Calculate total size.
  for (size_t c = 0; c < channels.size(); c++) {
    sz += strlen(channels[c].name.c_str()) + 1;  // +1 for \0
    sz += 16;                                    // 4 * int
  }
  data.resize(sz + 1);

  unsigned char *p = &data.at(0);

  for (size_t c = 0; c < channels.size(); c++) {
    memcpy(p, channels[c].name.c_str(), strlen(channels[c].name.c_str()));
    p += strlen(channels[c].name.c_str());
    (*p) = '\0';
    p++;

    int pixel_type = channels[c].pixel_type;
    int x_sampling = channels[c].x_sampling;
    int y_sampling = channels[c].y_sampling;
    tinyexr::swap4(reinterpret_cast<unsigned int *>(&pixel_type));
    tinyexr::swap4(reinterpret_cast<unsigned int *>(&x_sampling));
    tinyexr::swap4(reinterpret_cast<unsigned int *>(&y_sampling));

    memcpy(p, &pixel_type, sizeof(int));
    p += sizeof(int);

    (*p) = channels[c].p_linear;
    p += 4;

    memcpy(p, &x_sampling, sizeof(int));
    p += sizeof(int);

    memcpy(p, &y_sampling, sizeof(int));
    p += sizeof(int);
  }

  (*p) = '\0';
}


static void tdefl_start_dynamic_block(tdefl_compressor *d) {
  int num_lit_codes, num_dist_codes, num_bit_lengths;
  mz_uint i, total_code_sizes_to_pack, num_packed_code_sizes, rle_z_count,
      rle_repeat_count, packed_code_sizes_index;
  mz_uint8
      code_sizes_to_pack[TDEFL_MAX_HUFF_SYMBOLS_0 + TDEFL_MAX_HUFF_SYMBOLS_1],
      packed_code_sizes[TDEFL_MAX_HUFF_SYMBOLS_0 + TDEFL_MAX_HUFF_SYMBOLS_1],
      prev_code_size = 0xFF;

  d->m_huff_count[0][256] = 1;

  tdefl_optimize_huffman_table(d, 0, TDEFL_MAX_HUFF_SYMBOLS_0, 15, MZ_FALSE);
  tdefl_optimize_huffman_table(d, 1, TDEFL_MAX_HUFF_SYMBOLS_1, 15, MZ_FALSE);

  for (num_lit_codes = 286; num_lit_codes > 257; num_lit_codes--)
    if (d->m_huff_code_sizes[0][num_lit_codes - 1]) break;
  for (num_dist_codes = 30; num_dist_codes > 1; num_dist_codes--)
    if (d->m_huff_code_sizes[1][num_dist_codes - 1]) break;

  memcpy(code_sizes_to_pack, &d->m_huff_code_sizes[0][0], num_lit_codes);
  memcpy(code_sizes_to_pack + num_lit_codes, &d->m_huff_code_sizes[1][0],
         num_dist_codes);
  total_code_sizes_to_pack = num_lit_codes + num_dist_codes;
  num_packed_code_sizes = 0;
  rle_z_count = 0;
  rle_repeat_count = 0;

  memset(&d->m_huff_count[2][0], 0,
         sizeof(d->m_huff_count[2][0]) * TDEFL_MAX_HUFF_SYMBOLS_2);
  for (i = 0; i < total_code_sizes_to_pack; i++) {
    mz_uint8 code_size = code_sizes_to_pack[i];
    if (!code_size) {
      TDEFL_RLE_PREV_CODE_SIZE();
      if (++rle_z_count == 138) {
        TDEFL_RLE_ZERO_CODE_SIZE();
      }
    } else {
      TDEFL_RLE_ZERO_CODE_SIZE();
      if (code_size != prev_code_size) {
        TDEFL_RLE_PREV_CODE_SIZE();
        d->m_huff_count[2][code_size] =
            (mz_uint16)(d->m_huff_count[2][code_size] + 1);
        packed_code_sizes[num_packed_code_sizes++] = code_size;
      } else if (++rle_repeat_count == 6) {
        TDEFL_RLE_PREV_CODE_SIZE();
      }
    }
    prev_code_size = code_size;
  }
  if (rle_repeat_count) {
    TDEFL_RLE_PREV_CODE_SIZE();
  } else {
    TDEFL_RLE_ZERO_CODE_SIZE();
  }

  tdefl_optimize_huffman_table(d, 2, TDEFL_MAX_HUFF_SYMBOLS_2, 7, MZ_FALSE);

  TDEFL_PUT_BITS(2, 2);

  TDEFL_PUT_BITS(num_lit_codes - 257, 5);
  TDEFL_PUT_BITS(num_dist_codes - 1, 5);

  for (num_bit_lengths = 18; num_bit_lengths >= 0; num_bit_lengths--)
    if (d->m_huff_code_sizes
            [2][s_tdefl_packed_code_size_syms_swizzle[num_bit_lengths]])
      break;
  num_bit_lengths = MZ_MAX(4, (num_bit_lengths + 1));
  TDEFL_PUT_BITS(num_bit_lengths - 4, 4);
  for (i = 0; (int)i < num_bit_lengths; i++)
    TDEFL_PUT_BITS(
        d->m_huff_code_sizes[2][s_tdefl_packed_code_size_syms_swizzle[i]], 3);

  for (packed_code_sizes_index = 0;
       packed_code_sizes_index < num_packed_code_sizes;) {
    mz_uint code = packed_code_sizes[packed_code_sizes_index++];
    MZ_ASSERT(code < TDEFL_MAX_HUFF_SYMBOLS_2);
    TDEFL_PUT_BITS(d->m_huff_codes[2][code], d->m_huff_code_sizes[2][code]);
    if (code >= 16)
      TDEFL_PUT_BITS(packed_code_sizes[packed_code_sizes_index++],
                     "\02\03\07"[code - 16]);
  }
}


mz_zip_reader_filename_less(const mz_zip_array *pCentral_dir_array,
                            const mz_zip_array *pCentral_dir_offsets,
                            mz_uint l_index, mz_uint r_index) {
  const mz_uint8 *pL = &MZ_ZIP_ARRAY_ELEMENT(
                     pCentral_dir_array, mz_uint8,
                     MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_offsets, mz_uint32,
                                          l_index)),
                 *pE;
  const mz_uint8 *pR = &MZ_ZIP_ARRAY_ELEMENT(
      pCentral_dir_array, mz_uint8,
      MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_offsets, mz_uint32, r_index));
  mz_uint l_len = MZ_READ_LE16(pL + MZ_ZIP_CDH_FILENAME_LEN_OFS),
          r_len = MZ_READ_LE16(pR + MZ_ZIP_CDH_FILENAME_LEN_OFS);
  mz_uint8 l = 0, r = 0;
  pL += MZ_ZIP_CENTRAL_DIR_HEADER_SIZE;
  pR += MZ_ZIP_CENTRAL_DIR_HEADER_SIZE;
  pE = pL + MZ_MIN(l_len, r_len);
  while (pL < pE) {
    if ((l = MZ_TOLOWER(*pL)) != (r = MZ_TOLOWER(*pR))) break;
    pL++;
    pR++;
  }
  return (pL == pE) ? (l_len < r_len) : (l < r);
}


static size_t mz_zip_file_read_func(void *pOpaque, mz_uint64 file_ofs,
                                    void *pBuf, size_t n) {
  mz_zip_archive *pZip = (mz_zip_archive *)pOpaque;
  mz_int64 cur_ofs = MZ_FTELL64(pZip->m_pState->m_pFile);
  if (((mz_int64)file_ofs < 0) ||
      (((cur_ofs != (mz_int64)file_ofs)) &&
       (MZ_FSEEK64(pZip->m_pState->m_pFile, (mz_int64)file_ofs, SEEK_SET))))
    return 0;
  return MZ_FREAD(pBuf, 1, n, pZip->m_pState->m_pFile);
}


static void CompressRle(unsigned char *dst,
                        tinyexr::tinyexr_uint64 &compressedSize,
                        const unsigned char *src, unsigned long src_size) {
  std::vector<unsigned char> tmpBuf(src_size);

  //
  // Apply EXR-specific? postprocess. Grabbed from OpenEXR's
  // ImfRleCompressor.cpp
  //

  //
  // Reorder the pixel data.
  //

  const char *srcPtr = reinterpret_cast<const char *>(src);

  {
    char *t1 = reinterpret_cast<char *>(&tmpBuf.at(0));
    char *t2 = reinterpret_cast<char *>(&tmpBuf.at(0)) + (src_size + 1) / 2;
    const char *stop = srcPtr + src_size;

    for (;;) {
      if (srcPtr < stop)
        *(t1++) = *(srcPtr++);
      else
        break;

      if (srcPtr < stop)
        *(t2++) = *(srcPtr++);
      else
        break;
    }
  }

  //
  // Predictor.
  //

  {
    unsigned char *t = &tmpBuf.at(0) + 1;
    unsigned char *stop = &tmpBuf.at(0) + src_size;
    int p = t[-1];

    while (t < stop) {
      int d = int(t[0]) - p + (128 + 256);
      p = t[0];
      t[0] = static_cast<unsigned char>(d);
      ++t;
    }
  }

  // outSize will be (srcSiz * 3) / 2 at max.
  int outSize = rleCompress(static_cast<int>(src_size),
                            reinterpret_cast<const char *>(&tmpBuf.at(0)),
                            reinterpret_cast<signed char *>(dst));
  assert(outSize > 0);

  compressedSize = static_cast<tinyexr::tinyexr_uint64>(outSize);

  // Use uncompressed data when compressed data is larger than uncompressed.
  // (Issue 40)
  if (compressedSize >= src_size) {
    compressedSize = src_size;
    memcpy(dst, src, src_size);
  }
}


int mz_compress2(unsigned char *pDest, mz_ulong *pDest_len,
                 const unsigned char *pSource, mz_ulong source_len, int level) {
  int status;
  mz_stream stream;
  memset(&stream, 0, sizeof(stream));

  // In case mz_ulong is 64-bits (argh I hate longs).
  if ((source_len | *pDest_len) > 0xFFFFFFFFU) return MZ_PARAM_ERROR;

  stream.next_in = pSource;
  stream.avail_in = (mz_uint32)source_len;
  stream.next_out = pDest;
  stream.avail_out = (mz_uint32)*pDest_len;

  status = mz_deflateInit(&stream, level);
  if (status != MZ_OK) return status;

  status = mz_deflate(&stream, MZ_FINISH);
  if (status != MZ_STREAM_END) {
    mz_deflateEnd(&stream);
    return (status == MZ_OK) ? MZ_BUF_ERROR : status;
  }

  *pDest_len = stream.total_out;
  return mz_deflateEnd(&stream);
}


mz_bool mz_zip_writer_init_from_reader(mz_zip_archive *pZip,
                                       const char *pFilename) {
  mz_zip_internal_state *pState;
  if ((!pZip) || (!pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_READING))
    return MZ_FALSE;
  // No sense in trying to write to an archive that's already at the support max
  // size
  if ((pZip->m_total_files == 0xFFFF) ||
      ((pZip->m_archive_size + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE +
        MZ_ZIP_LOCAL_DIR_HEADER_SIZE) > 0xFFFFFFFF))
    return MZ_FALSE;

  pState = pZip->m_pState;

  if (pState->m_pFile) {
#ifdef MINIZ_NO_STDIO
    pFilename;
    return MZ_FALSE;
#else
    // Archive is being read from stdio - try to reopen as writable.
    if (pZip->m_pIO_opaque != pZip) return MZ_FALSE;
    if (!pFilename) return MZ_FALSE;
    pZip->m_pWrite = mz_zip_file_write_func;
    if (NULL ==
        (pState->m_pFile = MZ_FREOPEN(pFilename, "r+b", pState->m_pFile))) {
      // The mz_zip_archive is now in a bogus state because pState->m_pFile is
      // NULL, so just close it.
      mz_zip_reader_end(pZip);
      return MZ_FALSE;
    }
#endif  // #ifdef MINIZ_NO_STDIO
  } else if (pState->m_pMem) {
    // Archive lives in a memory block. Assume it's from the heap that we can
    // resize using the realloc callback.
    if (pZip->m_pIO_opaque != pZip) return MZ_FALSE;
    pState->m_mem_capacity = pState->m_mem_size;
    pZip->m_pWrite = mz_zip_heap_write_func;
  }
  // Archive is being read via a user provided read function - make sure the
  // user has specified a write function too.
  else if (!pZip->m_pWrite)
    return MZ_FALSE;

  // Start writing new files at the archive's current central directory
  // location.
  pZip->m_archive_size = pZip->m_central_directory_file_ofs;
  pZip->m_zip_mode = MZ_ZIP_MODE_WRITING;
  pZip->m_central_directory_file_ofs = 0;

  return MZ_TRUE;
}


int LoadEXRFromMemory(float **out_rgba, int *width, int *height,
                      const unsigned char *memory, size_t size,
                      const char **err) {
  if (out_rgba == NULL || memory == NULL) {
    tinyexr::SetErrorMessage("Invalid argument for LoadEXRFromMemory", err);
    return TINYEXR_ERROR_INVALID_ARGUMENT;
  }

  EXRVersion exr_version;
  EXRImage exr_image;
  EXRHeader exr_header;

  InitEXRHeader(&exr_header);

  int ret = ParseEXRVersionFromMemory(&exr_version, memory, size);
  if (ret != TINYEXR_SUCCESS) {
    tinyexr::SetErrorMessage("Failed to parse EXR version", err);
    return ret;
  }

  ret = ParseEXRHeaderFromMemory(&exr_header, &exr_version, memory, size, err);
  if (ret != TINYEXR_SUCCESS) {
    return ret;
  }

  // Read HALF channel as FLOAT.
  for (int i = 0; i < exr_header.num_channels; i++) {
    if (exr_header.pixel_types[i] == TINYEXR_PIXELTYPE_HALF) {
      exr_header.requested_pixel_types[i] = TINYEXR_PIXELTYPE_FLOAT;
    }
  }

  InitEXRImage(&exr_image);
  ret = LoadEXRImageFromMemory(&exr_image, &exr_header, memory, size, err);
  if (ret != TINYEXR_SUCCESS) {
    return ret;
  }

  // RGBA
  int idxR = -1;
  int idxG = -1;
  int idxB = -1;
  int idxA = -1;
  for (int c = 0; c < exr_header.num_channels; c++) {
    if (strcmp(exr_header.channels[c].name, "R") == 0) {
      idxR = c;
    } else if (strcmp(exr_header.channels[c].name, "G") == 0) {
      idxG = c;
    } else if (strcmp(exr_header.channels[c].name, "B") == 0) {
      idxB = c;
    } else if (strcmp(exr_header.channels[c].name, "A") == 0) {
      idxA = c;
    }
  }

  // TODO(syoyo): Refactor removing same code as used in LoadEXR().
  if (exr_header.num_channels == 1) {
    // Grayscale channel only.

    (*out_rgba) = reinterpret_cast<float *>(
        malloc(4 * sizeof(float) * static_cast<size_t>(exr_image.width) *
               static_cast<size_t>(exr_image.height)));

    if (exr_header.tiled) {
      for (int it = 0; it < exr_image.num_tiles; it++) {
        for (int j = 0; j < exr_header.tile_size_y; j++) {
          for (int i = 0; i < exr_header.tile_size_x; i++) {
            const int ii =
                exr_image.tiles[it].offset_x * exr_header.tile_size_x + i;
            const int jj =
                exr_image.tiles[it].offset_y * exr_header.tile_size_y + j;
            const int idx = ii + jj * exr_image.width;

            // out of region check.
            if (ii >= exr_image.width) {
              continue;
            }
            if (jj >= exr_image.height) {
              continue;
            }
            const int srcIdx = i + j * exr_header.tile_size_x;
            unsigned char **src = exr_image.tiles[it].images;
            (*out_rgba)[4 * idx + 0] =
                reinterpret_cast<float **>(src)[0][srcIdx];
            (*out_rgba)[4 * idx + 1] =
                reinterpret_cast<float **>(src)[0][srcIdx];
            (*out_rgba)[4 * idx + 2] =
                reinterpret_cast<float **>(src)[0][srcIdx];
            (*out_rgba)[4 * idx + 3] =
                reinterpret_cast<float **>(src)[0][srcIdx];
          }
        }
      }
    } else {
      for (int i = 0; i < exr_image.width * exr_image.height; i++) {
        const float val = reinterpret_cast<float **>(exr_image.images)[0][i];
        (*out_rgba)[4 * i + 0] = val;
        (*out_rgba)[4 * i + 1] = val;
        (*out_rgba)[4 * i + 2] = val;
        (*out_rgba)[4 * i + 3] = val;
      }
    }

  } else {
    // TODO(syoyo): Support non RGBA image.

    if (idxR == -1) {
      tinyexr::SetErrorMessage("R channel not found", err);

      // @todo { free exr_image }
      return TINYEXR_ERROR_INVALID_DATA;
    }

    if (idxG == -1) {
      tinyexr::SetErrorMessage("G channel not found", err);
      // @todo { free exr_image }
      return TINYEXR_ERROR_INVALID_DATA;
    }

    if (idxB == -1) {
      tinyexr::SetErrorMessage("B channel not found", err);
      // @todo { free exr_image }
      return TINYEXR_ERROR_INVALID_DATA;
    }

    (*out_rgba) = reinterpret_cast<float *>(
        malloc(4 * sizeof(float) * static_cast<size_t>(exr_image.width) *
               static_cast<size_t>(exr_image.height)));

    if (exr_header.tiled) {
      for (int it = 0; it < exr_image.num_tiles; it++) {
        for (int j = 0; j < exr_header.tile_size_y; j++)
          for (int i = 0; i < exr_header.tile_size_x; i++) {
            const int ii =
                exr_image.tiles[it].offset_x * exr_header.tile_size_x + i;
            const int jj =
                exr_image.tiles[it].offset_y * exr_header.tile_size_y + j;
            const int idx = ii + jj * exr_image.width;

            // out of region check.
            if (ii >= exr_image.width) {
              continue;
            }
            if (jj >= exr_image.height) {
              continue;
            }
            const int srcIdx = i + j * exr_header.tile_size_x;
            unsigned char **src = exr_image.tiles[it].images;
            (*out_rgba)[4 * idx + 0] =
                reinterpret_cast<float **>(src)[idxR][srcIdx];
            (*out_rgba)[4 * idx + 1] =
                reinterpret_cast<float **>(src)[idxG][srcIdx];
            (*out_rgba)[4 * idx + 2] =
                reinterpret_cast<float **>(src)[idxB][srcIdx];
            if (idxA != -1) {
              (*out_rgba)[4 * idx + 3] =
                  reinterpret_cast<float **>(src)[idxA][srcIdx];
            } else {
              (*out_rgba)[4 * idx + 3] = 1.0;
            }
          }
      }
    } else {
      for (int i = 0; i < exr_image.width * exr_image.height; i++) {
        (*out_rgba)[4 * i + 0] =
            reinterpret_cast<float **>(exr_image.images)[idxR][i];
        (*out_rgba)[4 * i + 1] =
            reinterpret_cast<float **>(exr_image.images)[idxG][i];
        (*out_rgba)[4 * i + 2] =
            reinterpret_cast<float **>(exr_image.images)[idxB][i];
        if (idxA != -1) {
          (*out_rgba)[4 * i + 3] =
              reinterpret_cast<float **>(exr_image.images)[idxA][i];
        } else {
          (*out_rgba)[4 * i + 3] = 1.0;
        }
      }
    }
  }

  (*width) = exr_image.width;
  (*height) = exr_image.height;

  FreeEXRHeader(&exr_header);
  FreeEXRImage(&exr_image);

  return TINYEXR_SUCCESS;
}


static mz_bool mz_zip_reader_read_central_dir(mz_zip_archive *pZip,
                                              mz_uint32 flags) {
  mz_uint cdir_size, num_this_disk, cdir_disk_index;
  mz_uint64 cdir_ofs;
  mz_int64 cur_file_ofs;
  const mz_uint8 *p;
  mz_uint32 buf_u32[4096 / sizeof(mz_uint32)];
  mz_uint8 *pBuf = (mz_uint8 *)buf_u32;
  mz_bool sort_central_dir =
      ((flags & MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY) == 0);
  // Basic sanity checks - reject files which are too small, and check the first
  // 4 bytes of the file to make sure a local header is there.
  if (pZip->m_archive_size < MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE)
    return MZ_FALSE;
  // Find the end of central directory record by scanning the file from the end
  // towards the beginning.
  cur_file_ofs =
      MZ_MAX((mz_int64)pZip->m_archive_size - (mz_int64)sizeof(buf_u32), 0);
  for (;;) {
    int i,
        n = (int)MZ_MIN(sizeof(buf_u32), pZip->m_archive_size - cur_file_ofs);
    if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pBuf, n) != (mz_uint)n)
      return MZ_FALSE;
    for (i = n - 4; i >= 0; --i)
      if (MZ_READ_LE32(pBuf + i) == MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIG) break;
    if (i >= 0) {
      cur_file_ofs += i;
      break;
    }
    if ((!cur_file_ofs) || ((pZip->m_archive_size - cur_file_ofs) >=
                            (0xFFFF + MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE)))
      return MZ_FALSE;
    cur_file_ofs = MZ_MAX(cur_file_ofs - (sizeof(buf_u32) - 3), 0);
  }
  // Read and verify the end of central directory record.
  if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pBuf,
                    MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) !=
      MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE)
    return MZ_FALSE;
  if ((MZ_READ_LE32(pBuf + MZ_ZIP_ECDH_SIG_OFS) !=
       MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIG) ||
      ((pZip->m_total_files =
            MZ_READ_LE16(pBuf + MZ_ZIP_ECDH_CDIR_TOTAL_ENTRIES_OFS)) !=
       MZ_READ_LE16(pBuf + MZ_ZIP_ECDH_CDIR_NUM_ENTRIES_ON_DISK_OFS)))
    return MZ_FALSE;

  num_this_disk = MZ_READ_LE16(pBuf + MZ_ZIP_ECDH_NUM_THIS_DISK_OFS);
  cdir_disk_index = MZ_READ_LE16(pBuf + MZ_ZIP_ECDH_NUM_DISK_CDIR_OFS);
  if (((num_this_disk | cdir_disk_index) != 0) &&
      ((num_this_disk != 1) || (cdir_disk_index != 1)))
    return MZ_FALSE;

  if ((cdir_size = MZ_READ_LE32(pBuf + MZ_ZIP_ECDH_CDIR_SIZE_OFS)) <
      pZip->m_total_files * MZ_ZIP_CENTRAL_DIR_HEADER_SIZE)
    return MZ_FALSE;

  cdir_ofs = MZ_READ_LE32(pBuf + MZ_ZIP_ECDH_CDIR_OFS_OFS);
  if ((cdir_ofs + (mz_uint64)cdir_size) > pZip->m_archive_size) return MZ_FALSE;

  pZip->m_central_directory_file_ofs = cdir_ofs;

  if (pZip->m_total_files) {
    mz_uint i, n;

    // Read the entire central directory into a heap block, and allocate another
    // heap block to hold the unsorted central dir file record offsets, and
    // another to hold the sorted indices.
    if ((!mz_zip_array_resize(pZip, &pZip->m_pState->m_central_dir, cdir_size,
                              MZ_FALSE)) ||
        (!mz_zip_array_resize(pZip, &pZip->m_pState->m_central_dir_offsets,
                              pZip->m_total_files, MZ_FALSE)))
      return MZ_FALSE;

    if (sort_central_dir) {
      if (!mz_zip_array_resize(pZip,
                               &pZip->m_pState->m_sorted_central_dir_offsets,
                               pZip->m_total_files, MZ_FALSE))
        return MZ_FALSE;
    }

    if (pZip->m_pRead(pZip->m_pIO_opaque, cdir_ofs,
                      pZip->m_pState->m_central_dir.m_p,
                      cdir_size) != cdir_size)
      return MZ_FALSE;

    // Now create an index into the central directory file records, do some
    // basic sanity checking on each record, and check for zip64 entries (which
    // are not yet supported).
    p = (const mz_uint8 *)pZip->m_pState->m_central_dir.m_p;
    for (n = cdir_size, i = 0; i < pZip->m_total_files; ++i) {
      mz_uint total_header_size, comp_size, decomp_size, disk_index;
      if ((n < MZ_ZIP_CENTRAL_DIR_HEADER_SIZE) ||
          (MZ_READ_LE32(p) != MZ_ZIP_CENTRAL_DIR_HEADER_SIG))
        return MZ_FALSE;
      MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir_offsets, mz_uint32,
                           i) =
          (mz_uint32)(p - (const mz_uint8 *)pZip->m_pState->m_central_dir.m_p);
      if (sort_central_dir)
        MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_sorted_central_dir_offsets,
                             mz_uint32, i) = i;
      comp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_COMPRESSED_SIZE_OFS);
      decomp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS);
      if (((!MZ_READ_LE32(p + MZ_ZIP_CDH_METHOD_OFS)) &&
           (decomp_size != comp_size)) ||
          (decomp_size && !comp_size) || (decomp_size == 0xFFFFFFFF) ||
          (comp_size == 0xFFFFFFFF))
        return MZ_FALSE;
      disk_index = MZ_READ_LE16(p + MZ_ZIP_CDH_DISK_START_OFS);
      if ((disk_index != num_this_disk) && (disk_index != 1)) return MZ_FALSE;
      if (((mz_uint64)MZ_READ_LE32(p + MZ_ZIP_CDH_LOCAL_HEADER_OFS) +
           MZ_ZIP_LOCAL_DIR_HEADER_SIZE + comp_size) > pZip->m_archive_size)
        return MZ_FALSE;
      if ((total_header_size = MZ_ZIP_CENTRAL_DIR_HEADER_SIZE +
                               MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS) +
                               MZ_READ_LE16(p + MZ_ZIP_CDH_EXTRA_LEN_OFS) +
                               MZ_READ_LE16(p + MZ_ZIP_CDH_COMMENT_LEN_OFS)) >
          n)
        return MZ_FALSE;
      n -= total_header_size;
      p += total_header_size;
    }
  }

  if (sort_central_dir)
    mz_zip_reader_sort_central_dir_offsets_by_filename(pZip);

  return MZ_TRUE;
}


static void bitmapFromData(const unsigned short data[/*nData*/], int nData,
                           unsigned char bitmap[BITMAP_SIZE],
                           unsigned short &minNonZero,
                           unsigned short &maxNonZero) {
  for (int i = 0; i < BITMAP_SIZE; ++i) bitmap[i] = 0;

  for (int i = 0; i < nData; ++i) bitmap[data[i] >> 3] |= (1 << (data[i] & 7));

  bitmap[0] &= ~1;  // zero is not explicitly stored in
                    // the bitmap; we assume that the
                    // data always contain zeroes
  minNonZero = BITMAP_SIZE - 1;
  maxNonZero = 0;

  for (int i = 0; i < BITMAP_SIZE; ++i) {
    if (bitmap[i]) {
      if (minNonZero > i) minNonZero = i;
      if (maxNonZero < i) maxNonZero = i;
    }
  }
}


static MZ_FORCEINLINE void tdefl_record_match(tdefl_compressor *d,
                                              mz_uint match_len,
                                              mz_uint match_dist) {
  mz_uint32 s0, s1;

  MZ_ASSERT((match_len >= TDEFL_MIN_MATCH_LEN) && (match_dist >= 1) &&
            (match_dist <= TDEFL_LZ_DICT_SIZE));

  d->m_total_lz_bytes += match_len;

  d->m_pLZ_code_buf[0] = (mz_uint8)(match_len - TDEFL_MIN_MATCH_LEN);

  match_dist -= 1;
  d->m_pLZ_code_buf[1] = (mz_uint8)(match_dist & 0xFF);
  d->m_pLZ_code_buf[2] = (mz_uint8)(match_dist >> 8);
  d->m_pLZ_code_buf += 3;

  *d->m_pLZ_flags = (mz_uint8)((*d->m_pLZ_flags >> 1) | 0x80);
  if (--d->m_num_flags_left == 0) {
    d->m_num_flags_left = 8;
    d->m_pLZ_flags = d->m_pLZ_code_buf++;
  }

  s0 = s_tdefl_small_dist_sym[match_dist & 511];
  s1 = s_tdefl_large_dist_sym[(match_dist >> 8) & 127];
  d->m_huff_count[1][(match_dist < 512) ? s0 : s1]++;

  if (match_len >= TDEFL_MIN_MATCH_LEN)
    d->m_huff_count[0][s_tdefl_len_sym[match_len - TDEFL_MIN_MATCH_LEN]]++;
}


mz_ulong mz_deflateBound(mz_streamp pStream, mz_ulong source_len) {
  (void)pStream;
  // This is really over conservative. (And lame, but it's actually pretty
  // tricky to compute a true upper bound given the way tdefl's blocking works.)
  return MZ_MAX(128 + (source_len * 110) / 100,
                128 + source_len + ((source_len / (31 * 1024)) + 1) * 5);
}


static MZ_FORCEINLINE mz_bool mz_zip_array_reserve(mz_zip_archive *pZip,
                                                   mz_zip_array *pArray,
                                                   size_t new_capacity,
                                                   mz_uint growing) {
  if (new_capacity > pArray->m_capacity) {
    if (!mz_zip_array_ensure_capacity(pZip, pArray, new_capacity, growing))
      return MZ_FALSE;
  }
  return MZ_TRUE;
}


mz_uint mz_zip_reader_get_num_files(mz_zip_archive *pZip) {
  return pZip ? pZip->m_total_files : 0;
}