<commit_before>/*
 * Copyright 2020 Google Inc.
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

#include "gm/gm.h"
#include "include/core/SkCanvas.h"
#include "include/core/SkImage.h"
#include "include/core/SkStream.h"
#include "src/core/SkCompressedDataUtils.h"
#include "src/core/SkMipMap.h"
#include "src/gpu/GrContextPriv.h"
#include "src/gpu/gl/GrGLDefines.h"
#include "src/image/SkImage_Base.h"

#include "tools/Resources.h"

//-------------------------------------------------------------------------------------------------
struct ImageInfo {
    SkISize                  fDim;
    GrMipMapped              fMipMapped;
    SkImage::CompressionType fCompressionType;
};

/*
 * Get an int from a buffer
 * This method is unsafe, the caller is responsible for performing a check
 */
static inline uint32_t get_uint(uint8_t* buffer, uint32_t i) {
    uint32_t result;
    memcpy(&result, &(buffer[i]), 4);
    return result;
}

// This KTX loader is barely sufficient to load the specific files this GM requires. Use
// at your own peril.
static sk_sp<SkData> load_ktx(const char* filename, ImageInfo* imageInfo) {
    SkFILEStream input(filename);
    if (!input.isValid()) {
        return nullptr;
    }

    constexpr int kKTXIdentifierSize = 12;
    constexpr int kKTXHeaderSize = kKTXIdentifierSize + 13 * sizeof(uint32_t);
    uint8_t header[kKTXHeaderSize];

    if (input.read(header, kKTXHeaderSize) != kKTXHeaderSize) {
        return nullptr;
    }

    static const uint8_t kExpectedIdentifier[kKTXIdentifierSize] = {
        0xAB, 0x4B, 0x54, 0x58, 0x20, 0x31, 0x31, 0xBB, 0x0D, 0x0A, 0x1A, 0x0A
    };

    if (memcmp(header, kExpectedIdentifier, kKTXIdentifierSize)) {
        return nullptr;
    }

    uint32_t endianness = get_uint(header, 12);
    if (endianness != 0x04030201) {
        // TODO: need to swap rest of header and, if glTypeSize is > 1, all
        // the texture data.
        return nullptr;
    }

    uint32_t glType = get_uint(header, 16);
    SkDEBUGCODE(uint32_t glTypeSize = get_uint(header, 20);)
    uint32_t glFormat = get_uint(header, 24);
    uint32_t glInternalFormat = get_uint(header, 28);
    //uint32_t glBaseInternalFormat = get_uint(header, 32);
    uint32_t pixelWidth = get_uint(header, 36);
    uint32_t pixelHeight = get_uint(header, 40);
    uint32_t pixelDepth = get_uint(header, 44);
    //uint32_t numberOfArrayElements = get_uint(header, 48);
    uint32_t numberOfFaces = get_uint(header, 52);
    int numberOfMipmapLevels = get_uint(header, 56);
    uint32_t bytesOfKeyValueData = get_uint(header, 60);

    if (glType != 0 || glFormat != 0) {  // only care about compressed data for now
        return nullptr;
    }
    SkASSERT(glTypeSize == 1); // required for compressed data

    // We only handle these four formats right now
    switch (glInternalFormat) {
        case GR_GL_COMPRESSED_ETC1_RGB8:
        case GR_GL_COMPRESSED_RGB8_ETC2:
            imageInfo->fCompressionType = SkImage::CompressionType::kETC2_RGB8_UNORM;
            break;
        case GR_GL_COMPRESSED_RGB_S3TC_DXT1_EXT:
            imageInfo->fCompressionType = SkImage::CompressionType::kBC1_RGB8_UNORM;
            break;
        case GR_GL_COMPRESSED_RGBA_S3TC_DXT1_EXT:
            imageInfo->fCompressionType = SkImage::CompressionType::kBC1_RGBA8_UNORM;
            break;
        default:
            return nullptr;
    }

    imageInfo->fDim.fWidth = pixelWidth;
    imageInfo->fDim.fHeight = pixelHeight;

    if (pixelDepth != 0) {
        return nullptr; // pixel depth is always zero for 2D textures
    }

    if (numberOfFaces != 1) {
        return nullptr; // we don't support cube maps right now
    }

    if (numberOfMipmapLevels == 1) {
        imageInfo->fMipMapped = GrMipMapped::kNo;
    } else {
        int numRequiredMipLevels = SkMipMap::ComputeLevelCount(pixelWidth, pixelHeight)+1;
        if (numberOfMipmapLevels != numRequiredMipLevels) {
            return nullptr;
        }
        imageInfo->fMipMapped = GrMipMapped::kYes;
    }

    if (bytesOfKeyValueData != 0) {
        return nullptr;
    }

    SkTArray<size_t> individualMipOffsets(numberOfMipmapLevels);

    size_t dataSize = SkCompressedDataSize(imageInfo->fCompressionType,
                                           { (int) pixelWidth, (int) pixelHeight },
                                           &individualMipOffsets,
                                           imageInfo->fMipMapped == GrMipMapped::kYes);
    SkASSERT(individualMipOffsets.size() == (size_t) numberOfMipmapLevels);

    sk_sp<SkData> data = SkData::MakeUninitialized(dataSize);

    uint8_t* dest = (uint8_t*) data->writable_data();

    size_t offset = 0;
    for (int i = 0; i < numberOfMipmapLevels; ++i) {
        uint32_t imageSize;

        if (input.read(&imageSize, 4) != 4) {
            return nullptr;
        }

        SkASSERT(offset + imageSize <= dataSize);
        SkASSERT(offset == individualMipOffsets[i]);

        if (input.read(&dest[offset], imageSize) != imageSize) {
            return nullptr;
        }

        offset += imageSize;
    }

    return data;
}

//-------------------------------------------------------------------------------------------------
typedef uint32_t DWORD;

// Values for the DDS_PIXELFORMAT 'dwFlags' field
constexpr unsigned int kDDPF_FOURCC      = 0x4;

struct DDS_PIXELFORMAT {
    DWORD dwSize;
    DWORD dwFlags;
    DWORD dwFourCC;
    DWORD dwRGBBitCount;
    DWORD dwRBitMask;
    DWORD dwGBitMask;
    DWORD dwBBitMask;
    DWORD dwABitMask;
};

// Values for the DDS_HEADER 'dwFlags' field
constexpr unsigned int kDDSD_CAPS        = 0x1;        // required
constexpr unsigned int kDDSD_HEIGHT      = 0x2;        // required
constexpr unsigned int kDDSD_WIDTH       = 0x4;        // required
constexpr unsigned int kDDSD_PITCH       = 0x8;
constexpr unsigned int kDDSD_PIXELFORMAT = 0x001000;   // required
constexpr unsigned int kDDSD_MIPMAPCOUNT = 0x020000;
constexpr unsigned int kDDSD_LINEARSIZE  = 0x080000;
constexpr unsigned int kDDSD_DEPTH       = 0x800000;

constexpr unsigned int kDDSD_REQUIRED = kDDSD_CAPS | kDDSD_HEIGHT | kDDSD_WIDTH | kDDSD_PIXELFORMAT;

typedef struct {
    DWORD           dwSize;
    DWORD           dwFlags;
    DWORD           dwHeight;
    DWORD           dwWidth;
    DWORD           dwPitchOrLinearSize;
    DWORD           dwDepth;
    DWORD           dwMipMapCount;
    DWORD           dwReserved1[11];
    DDS_PIXELFORMAT ddspf;
    DWORD           dwCaps;
    DWORD           dwCaps2;
    DWORD           dwCaps3;
    DWORD           dwCaps4;
    DWORD           dwReserved2;
} DDS_HEADER;

// This DDS loader is barely sufficient to load the specific files this GM requires. Use
// at your own peril.
static sk_sp<SkData> load_dds(const char* filename, ImageInfo* imageInfo) {
    SkFILEStream input(filename);
    if (!input.isValid()) {
        return nullptr;
    }

    constexpr uint32_t kMagic = 0x20534444;
    uint32_t magic;

    if (input.read(&magic, 4) != 4) {
        return nullptr;
    }

    if (magic != kMagic) {
        return nullptr;
    }

    constexpr size_t kDDSHeaderSize = sizeof(DDS_HEADER);
    static_assert(kDDSHeaderSize == 124);
    constexpr size_t kDDSPixelFormatSize = sizeof(DDS_PIXELFORMAT);
    static_assert(kDDSPixelFormatSize == 32);

    DDS_HEADER header;

    if (input.read(&header, kDDSHeaderSize) != kDDSHeaderSize) {
        return nullptr;
    }

    if (header.dwSize != kDDSHeaderSize ||
        header.ddspf.dwSize != kDDSPixelFormatSize) {
        return nullptr;
    }

    if ((header.dwFlags & kDDSD_REQUIRED) != kDDSD_REQUIRED) {
        return nullptr;
    }

    if (header.dwFlags & (kDDSD_PITCH | kDDSD_LINEARSIZE | kDDSD_DEPTH)) {
        // TODO: support these features
    }

    imageInfo->fDim.fWidth = header.dwWidth;
    imageInfo->fDim.fHeight = header.dwHeight;

    int numberOfMipmapLevels = 1;
    if (header.dwFlags & kDDSD_MIPMAPCOUNT) {
        if (header.dwMipMapCount == 1) {
            imageInfo->fMipMapped = GrMipMapped::kNo;
        } else {
            int numRequiredLevels = SkMipMap::ComputeLevelCount(header.dwWidth, header.dwHeight)+1;
            if (header.dwMipMapCount != (unsigned) numRequiredLevels) {
                return nullptr;
            }
            imageInfo->fMipMapped = GrMipMapped::kYes;
            numberOfMipmapLevels = numRequiredLevels;
        }
    } else {
        imageInfo->fMipMapped = GrMipMapped::kNo;
    }

    if (!(header.ddspf.dwFlags & kDDPF_FOURCC)) {
        return nullptr;
    }

    // We only handle these one format right now
    switch (header.ddspf.dwFourCC) {
        case 0x31545844: // DXT1
            imageInfo->fCompressionType = SkImage::CompressionType::kBC1_RGB8_UNORM;
            break;
        default:
            return nullptr;
    }

    SkTArray<size_t> individualMipOffsets(numberOfMipmapLevels);

    size_t dataSize = SkCompressedDataSize(imageInfo->fCompressionType,
                                           { (int) header.dwWidth, (int) header.dwHeight },
                                           &individualMipOffsets,
                                           imageInfo->fMipMapped == GrMipMapped::kYes);
    SkASSERT(individualMipOffsets.size() == (size_t) numberOfMipmapLevels);

    sk_sp<SkData> data = SkData::MakeUninitialized(dataSize);

    uint8_t* dest = (uint8_t*) data->writable_data();

    size_t amountRead = input.read(dest, dataSize);
    if (amountRead != dataSize) {
        return nullptr;
    }

    return data;
}

//-------------------------------------------------------------------------------------------------
static sk_sp<SkImage> data_to_img(GrContext *context, sk_sp<SkData> data, const ImageInfo& info) {
    if (context) {
        return SkImage::MakeTextureFromCompressed(context, std::move(data),
                                                  info.fDim.fWidth,
                                                  info.fDim.fHeight,
                                                  info.fCompressionType,
                                                  info.fMipMapped);
    } else {
        return SkImage::MakeRasterFromCompressed(std::move(data),
                                                 info.fDim.fWidth,
                                                 info.fDim.fHeight,
                                                 info.fCompressionType);
    }
}

namespace skiagm {

// This GM exercises our handling of some of the more exotic formats using externally
// generated content. Right now it only tests ETC1 and BC1.
class ExoticFormatsGM : public GM {
public:
    ExoticFormatsGM() {
        this->setBGColor(0xFFCCCCCC);
    }

protected:
    SkString onShortName() override {
        return SkString("exoticformats");
    }

    SkISize onISize() override {
        return SkISize::Make(2*kImgWidthHeight, kImgWidthHeight);
    }

    void loadImages(GrContext *context) {

        if (!fETC1Image) {
            ImageInfo info;
            sk_sp<SkData> data = load_ktx(GetResourcePath("images/flower-etc1.ktx").c_str(), &info);
            if (data) {
                SkASSERT(info.fDim.equals(kImgWidthHeight, kImgWidthHeight));
                SkASSERT(info.fMipMapped == GrMipMapped::kNo);
                SkASSERT(info.fCompressionType == SkImage::CompressionType::kETC2_RGB8_UNORM);

                fETC1Image = data_to_img(context, std::move(data), info);
            } else {
                SkDebugf("failed to load flower-etc1.ktx\n");
            }
        }

        if (!fBC1Image) {
            ImageInfo info;
            sk_sp<SkData> data = load_dds(GetResourcePath("images/flower-bc1.dds").c_str(), &info);
            if (data) {
                SkASSERT(info.fDim.equals(kImgWidthHeight, kImgWidthHeight));
                SkASSERT(info.fMipMapped == GrMipMapped::kNo);
                SkASSERT(info.fCompressionType == SkImage::CompressionType::kBC1_RGB8_UNORM);

                fBC1Image = data_to_img(context, std::move(data), info);
            } else {
                SkDebugf("failed to load flower-bc1.dds\n");
            }
        }

    }

    void drawImage(GrContext* context, SkCanvas* canvas, SkImage* image, int x, int y) {
        if (!image) {
            return;
        }

        bool isCompressed = false;
        if (image->isTextureBacked()) {
            const GrCaps* caps = context->priv().caps();

            GrTextureProxy* proxy = as_IB(image)->peekProxy();
            isCompressed = caps->isFormatCompressed(proxy->backendFormat());
        }

        canvas->drawImage(image, x, y);

        if (!isCompressed) {
            // Make it obvious which drawImages used decompressed images
            SkRect r = SkRect::MakeXYWH(x, y, kImgWidthHeight, kImgWidthHeight);
            SkPaint paint;
            paint.setColor(SK_ColorRED);
            paint.setStyle(SkPaint::kStroke_Style);
            canvas->drawRect(r, paint);
        }
    }

    void onDraw(SkCanvas* canvas) override {
        GrContext* context = canvas->getGrContext();

        this->loadImages(context);

        this->drawImage(context, canvas, fETC1Image.get(), 0, 0);
        this->drawImage(context, canvas, fBC1Image.get(), kImgWidthHeight, 0);
    }

private:
    static const int kImgWidthHeight = 128;

    sk_sp<SkImage> fETC1Image;
    sk_sp<SkImage> fBC1Image;

    typedef GM INHERITED;
};

//////////////////////////////////////////////////////////////////////////////

DEF_GM(return new ExoticFormatsGM;)
}
<commit_msg>Make exoticformats GM a bit easier to triage<commit_after>/*
 * Copyright 2020 Google Inc.
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

#include "gm/gm.h"
#include "include/core/SkCanvas.h"
#include "include/core/SkImage.h"
#include "include/core/SkStream.h"
#include "src/core/SkCompressedDataUtils.h"
#include "src/core/SkMipMap.h"
#include "src/gpu/GrContextPriv.h"
#include "src/gpu/gl/GrGLDefines.h"
#include "src/image/SkImage_Base.h"

#include "tools/Resources.h"

//-------------------------------------------------------------------------------------------------
struct ImageInfo {
    SkISize                  fDim;
    GrMipMapped              fMipMapped;
    SkImage::CompressionType fCompressionType;
};

/*
 * Get an int from a buffer
 * This method is unsafe, the caller is responsible for performing a check
 */
static inline uint32_t get_uint(uint8_t* buffer, uint32_t i) {
    uint32_t result;
    memcpy(&result, &(buffer[i]), 4);
    return result;
}

// This KTX loader is barely sufficient to load the specific files this GM requires. Use
// at your own peril.
static sk_sp<SkData> load_ktx(const char* filename, ImageInfo* imageInfo) {
    SkFILEStream input(filename);
    if (!input.isValid()) {
        return nullptr;
    }

    constexpr int kKTXIdentifierSize = 12;
    constexpr int kKTXHeaderSize = kKTXIdentifierSize + 13 * sizeof(uint32_t);
    uint8_t header[kKTXHeaderSize];

    if (input.read(header, kKTXHeaderSize) != kKTXHeaderSize) {
        return nullptr;
    }

    static const uint8_t kExpectedIdentifier[kKTXIdentifierSize] = {
        0xAB, 0x4B, 0x54, 0x58, 0x20, 0x31, 0x31, 0xBB, 0x0D, 0x0A, 0x1A, 0x0A
    };

    if (memcmp(header, kExpectedIdentifier, kKTXIdentifierSize)) {
        return nullptr;
    }

    uint32_t endianness = get_uint(header, 12);
    if (endianness != 0x04030201) {
        // TODO: need to swap rest of header and, if glTypeSize is > 1, all
        // the texture data.
        return nullptr;
    }

    uint32_t glType = get_uint(header, 16);
    SkDEBUGCODE(uint32_t glTypeSize = get_uint(header, 20);)
    uint32_t glFormat = get_uint(header, 24);
    uint32_t glInternalFormat = get_uint(header, 28);
    //uint32_t glBaseInternalFormat = get_uint(header, 32);
    uint32_t pixelWidth = get_uint(header, 36);
    uint32_t pixelHeight = get_uint(header, 40);
    uint32_t pixelDepth = get_uint(header, 44);
    //uint32_t numberOfArrayElements = get_uint(header, 48);
    uint32_t numberOfFaces = get_uint(header, 52);
    int numberOfMipmapLevels = get_uint(header, 56);
    uint32_t bytesOfKeyValueData = get_uint(header, 60);

    if (glType != 0 || glFormat != 0) {  // only care about compressed data for now
        return nullptr;
    }
    SkASSERT(glTypeSize == 1); // required for compressed data

    // We only handle these four formats right now
    switch (glInternalFormat) {
        case GR_GL_COMPRESSED_ETC1_RGB8:
        case GR_GL_COMPRESSED_RGB8_ETC2:
            imageInfo->fCompressionType = SkImage::CompressionType::kETC2_RGB8_UNORM;
            break;
        case GR_GL_COMPRESSED_RGB_S3TC_DXT1_EXT:
            imageInfo->fCompressionType = SkImage::CompressionType::kBC1_RGB8_UNORM;
            break;
        case GR_GL_COMPRESSED_RGBA_S3TC_DXT1_EXT:
            imageInfo->fCompressionType = SkImage::CompressionType::kBC1_RGBA8_UNORM;
            break;
        default:
            return nullptr;
    }

    imageInfo->fDim.fWidth = pixelWidth;
    imageInfo->fDim.fHeight = pixelHeight;

    if (pixelDepth != 0) {
        return nullptr; // pixel depth is always zero for 2D textures
    }

    if (numberOfFaces != 1) {
        return nullptr; // we don't support cube maps right now
    }

    if (numberOfMipmapLevels == 1) {
        imageInfo->fMipMapped = GrMipMapped::kNo;
    } else {
        int numRequiredMipLevels = SkMipMap::ComputeLevelCount(pixelWidth, pixelHeight)+1;
        if (numberOfMipmapLevels != numRequiredMipLevels) {
            return nullptr;
        }
        imageInfo->fMipMapped = GrMipMapped::kYes;
    }

    if (bytesOfKeyValueData != 0) {
        return nullptr;
    }

    SkTArray<size_t> individualMipOffsets(numberOfMipmapLevels);

    size_t dataSize = SkCompressedDataSize(imageInfo->fCompressionType,
                                           { (int) pixelWidth, (int) pixelHeight },
                                           &individualMipOffsets,
                                           imageInfo->fMipMapped == GrMipMapped::kYes);
    SkASSERT(individualMipOffsets.size() == (size_t) numberOfMipmapLevels);

    sk_sp<SkData> data = SkData::MakeUninitialized(dataSize);

    uint8_t* dest = (uint8_t*) data->writable_data();

    size_t offset = 0;
    for (int i = 0; i < numberOfMipmapLevels; ++i) {
        uint32_t imageSize;

        if (input.read(&imageSize, 4) != 4) {
            return nullptr;
        }

        SkASSERT(offset + imageSize <= dataSize);
        SkASSERT(offset == individualMipOffsets[i]);

        if (input.read(&dest[offset], imageSize) != imageSize) {
            return nullptr;
        }

        offset += imageSize;
    }

    return data;
}

//-------------------------------------------------------------------------------------------------
typedef uint32_t DWORD;

// Values for the DDS_PIXELFORMAT 'dwFlags' field
constexpr unsigned int kDDPF_FOURCC      = 0x4;

struct DDS_PIXELFORMAT {
    DWORD dwSize;
    DWORD dwFlags;
    DWORD dwFourCC;
    DWORD dwRGBBitCount;
    DWORD dwRBitMask;
    DWORD dwGBitMask;
    DWORD dwBBitMask;
    DWORD dwABitMask;
};

// Values for the DDS_HEADER 'dwFlags' field
constexpr unsigned int kDDSD_CAPS        = 0x1;        // required
constexpr unsigned int kDDSD_HEIGHT      = 0x2;        // required
constexpr unsigned int kDDSD_WIDTH       = 0x4;        // required
constexpr unsigned int kDDSD_PITCH       = 0x8;
constexpr unsigned int kDDSD_PIXELFORMAT = 0x001000;   // required
constexpr unsigned int kDDSD_MIPMAPCOUNT = 0x020000;
constexpr unsigned int kDDSD_LINEARSIZE  = 0x080000;
constexpr unsigned int kDDSD_DEPTH       = 0x800000;

constexpr unsigned int kDDSD_REQUIRED = kDDSD_CAPS | kDDSD_HEIGHT | kDDSD_WIDTH | kDDSD_PIXELFORMAT;

typedef struct {
    DWORD           dwSize;
    DWORD           dwFlags;
    DWORD           dwHeight;
    DWORD           dwWidth;
    DWORD           dwPitchOrLinearSize;
    DWORD           dwDepth;
    DWORD           dwMipMapCount;
    DWORD           dwReserved1[11];
    DDS_PIXELFORMAT ddspf;
    DWORD           dwCaps;
    DWORD           dwCaps2;
    DWORD           dwCaps3;
    DWORD           dwCaps4;
    DWORD           dwReserved2;
} DDS_HEADER;

// This DDS loader is barely sufficient to load the specific files this GM requires. Use
// at your own peril.
static sk_sp<SkData> load_dds(const char* filename, ImageInfo* imageInfo) {
    SkFILEStream input(filename);
    if (!input.isValid()) {
        return nullptr;
    }

    constexpr uint32_t kMagic = 0x20534444;
    uint32_t magic;

    if (input.read(&magic, 4) != 4) {
        return nullptr;
    }

    if (magic != kMagic) {
        return nullptr;
    }

    constexpr size_t kDDSHeaderSize = sizeof(DDS_HEADER);
    static_assert(kDDSHeaderSize == 124);
    constexpr size_t kDDSPixelFormatSize = sizeof(DDS_PIXELFORMAT);
    static_assert(kDDSPixelFormatSize == 32);

    DDS_HEADER header;

    if (input.read(&header, kDDSHeaderSize) != kDDSHeaderSize) {
        return nullptr;
    }

    if (header.dwSize != kDDSHeaderSize ||
        header.ddspf.dwSize != kDDSPixelFormatSize) {
        return nullptr;
    }

    if ((header.dwFlags & kDDSD_REQUIRED) != kDDSD_REQUIRED) {
        return nullptr;
    }

    if (header.dwFlags & (kDDSD_PITCH | kDDSD_LINEARSIZE | kDDSD_DEPTH)) {
        // TODO: support these features
    }

    imageInfo->fDim.fWidth = header.dwWidth;
    imageInfo->fDim.fHeight = header.dwHeight;

    int numberOfMipmapLevels = 1;
    if (header.dwFlags & kDDSD_MIPMAPCOUNT) {
        if (header.dwMipMapCount == 1) {
            imageInfo->fMipMapped = GrMipMapped::kNo;
        } else {
            int numRequiredLevels = SkMipMap::ComputeLevelCount(header.dwWidth, header.dwHeight)+1;
            if (header.dwMipMapCount != (unsigned) numRequiredLevels) {
                return nullptr;
            }
            imageInfo->fMipMapped = GrMipMapped::kYes;
            numberOfMipmapLevels = numRequiredLevels;
        }
    } else {
        imageInfo->fMipMapped = GrMipMapped::kNo;
    }

    if (!(header.ddspf.dwFlags & kDDPF_FOURCC)) {
        return nullptr;
    }

    // We only handle these one format right now
    switch (header.ddspf.dwFourCC) {
        case 0x31545844: // DXT1
            imageInfo->fCompressionType = SkImage::CompressionType::kBC1_RGB8_UNORM;
            break;
        default:
            return nullptr;
    }

    SkTArray<size_t> individualMipOffsets(numberOfMipmapLevels);

    size_t dataSize = SkCompressedDataSize(imageInfo->fCompressionType,
                                           { (int) header.dwWidth, (int) header.dwHeight },
                                           &individualMipOffsets,
                                           imageInfo->fMipMapped == GrMipMapped::kYes);
    SkASSERT(individualMipOffsets.size() == (size_t) numberOfMipmapLevels);

    sk_sp<SkData> data = SkData::MakeUninitialized(dataSize);

    uint8_t* dest = (uint8_t*) data->writable_data();

    size_t amountRead = input.read(dest, dataSize);
    if (amountRead != dataSize) {
        return nullptr;
    }

    return data;
}

//-------------------------------------------------------------------------------------------------
static sk_sp<SkImage> data_to_img(GrContext *context, sk_sp<SkData> data, const ImageInfo& info) {
    if (context) {
        return SkImage::MakeTextureFromCompressed(context, std::move(data),
                                                  info.fDim.fWidth,
                                                  info.fDim.fHeight,
                                                  info.fCompressionType,
                                                  info.fMipMapped);
    } else {
        return SkImage::MakeRasterFromCompressed(std::move(data),
                                                 info.fDim.fWidth,
                                                 info.fDim.fHeight,
                                                 info.fCompressionType);
    }
}

namespace skiagm {

// This GM exercises our handling of some of the more exotic formats using externally
// generated content. Right now it only tests ETC1 and BC1.
class ExoticFormatsGM : public GM {
public:
    ExoticFormatsGM() {
        this->setBGColor(SK_ColorBLACK);
    }

protected:
    SkString onShortName() override {
        return SkString("exoticformats");
    }

    SkISize onISize() override {
        return SkISize::Make(2*kImgWidthHeight + 3 * kPad, kImgWidthHeight + 2 * kPad);
    }

    void loadImages(GrContext *context) {

        if (!fETC1Image) {
            ImageInfo info;
            sk_sp<SkData> data = load_ktx(GetResourcePath("images/flower-etc1.ktx").c_str(), &info);
            if (data) {
                SkASSERT(info.fDim.equals(kImgWidthHeight, kImgWidthHeight));
                SkASSERT(info.fMipMapped == GrMipMapped::kNo);
                SkASSERT(info.fCompressionType == SkImage::CompressionType::kETC2_RGB8_UNORM);

                fETC1Image = data_to_img(context, std::move(data), info);
            } else {
                SkDebugf("failed to load flower-etc1.ktx\n");
            }
        }

        if (!fBC1Image) {
            ImageInfo info;
            sk_sp<SkData> data = load_dds(GetResourcePath("images/flower-bc1.dds").c_str(), &info);
            if (data) {
                SkASSERT(info.fDim.equals(kImgWidthHeight, kImgWidthHeight));
                SkASSERT(info.fMipMapped == GrMipMapped::kNo);
                SkASSERT(info.fCompressionType == SkImage::CompressionType::kBC1_RGB8_UNORM);

                fBC1Image = data_to_img(context, std::move(data), info);
            } else {
                SkDebugf("failed to load flower-bc1.dds\n");
            }
        }

    }

    void drawImage(GrContext* context, SkCanvas* canvas, SkImage* image, int x, int y) {
        if (!image) {
            return;
        }

        bool isCompressed = false;
        if (image->isTextureBacked()) {
            const GrCaps* caps = context->priv().caps();

            GrTextureProxy* proxy = as_IB(image)->peekProxy();
            isCompressed = caps->isFormatCompressed(proxy->backendFormat());
        }

        canvas->drawImage(image, x, y);

        if (!isCompressed) {
            // Make it obvious which drawImages used decompressed images
            SkRect r = SkRect::MakeXYWH(x, y, kImgWidthHeight, kImgWidthHeight);
            SkPaint paint;
            paint.setColor(SK_ColorRED);
            paint.setStyle(SkPaint::kStroke_Style);
            paint.setStrokeWidth(2.0f);
            canvas->drawRect(r, paint);
        }
    }

    void onDraw(SkCanvas* canvas) override {
        GrContext* context = canvas->getGrContext();

        this->loadImages(context);

        this->drawImage(context, canvas, fETC1Image.get(), kPad, kPad);
        this->drawImage(context, canvas, fBC1Image.get(), kImgWidthHeight + 2 * kPad, kPad);
    }

private:
    static const int kImgWidthHeight = 128;
    static const int kPad = 4;

    sk_sp<SkImage> fETC1Image;
    sk_sp<SkImage> fBC1Image;

    typedef GM INHERITED;
};

//////////////////////////////////////////////////////////////////////////////

DEF_GM(return new ExoticFormatsGM;)
}
<|endoftext|>


<commit_before>#ifndef __scripting_function_hpp__
#define __scripting_function_hpp__

#include "scripting_object.hpp"

#include <string>
#include <boost/function.hpp>
#include <boost/bind.hpp>
#include <stdexcept>
#include <vector>

// handle_return implementations
template<typename Ret>
struct Handle_Return
{
  Scripting_Object operator()(const boost::function<Ret ()> &f)
  {
    return Scripting_Object(f());
  }
};

template<typename Ret>
struct Handle_Return<Ret &>
{
  Scripting_Object operator()(const boost::function<Ret &()> &f)
  {
    return Scripting_Object(boost::ref(f()));
  }
};

template<>
struct Handle_Return<void>
{
  Scripting_Object operator()(const boost::function<void ()> &f)
  {
    f();
    return Scripting_Object();
  }
};

// call_func implementations todo: handle reference return types
// to be made variadic
template<typename Ret, typename Param1, typename Param2>
Scripting_Object call_func(const boost::function<Ret (Param1, Param2)> &f, const std::vector<Scripting_Object> &params)
{
  if (params.size() != 2)
  {
    throw std::range_error("Incorrect number of parameters");
  } else {
    return Handle_Return<Ret>()(boost::bind(f, Cast_Helper<Param1>()(params[0]), Cast_Helper<Param2>()(params[1])));
  }
}

template<typename Ret, typename Param1>
Scripting_Object call_func(const boost::function<Ret (Param1)> &f, const std::vector<Scripting_Object> &params)
{
  if (params.size() != 1)
  {
    throw std::range_error("Incorrect number of parameters");
  } else {
    return Handle_Return<Ret>()(boost::bind(f, Cast_Helper<Param1>()(params[0])));
  }
}

template<typename Ret>
Scripting_Object call_func(const boost::function<Ret ()> &f, const std::vector<Scripting_Object> &params)
{
  if (params.size() != 0)
  {
    throw std::range_error("Incorrect number of parameters");
  } else {
    return Handle_Return<Ret>()(f);
  }
}

class Function_Handler
{
  public:
    virtual Scripting_Object operator()(const std::vector<Scripting_Object> &params) = 0;
};

template<typename Func>
class Function_Handler_Impl : public Function_Handler
{
  public:
    Function_Handler_Impl(const Func &f)
      : m_f(f)
    {
    }

    virtual Scripting_Object operator()(const std::vector<Scripting_Object> &params)
    {
      return call_func(m_f, params);
    }

  private:
    Func m_f;
};

std::vector<Scripting_Object> build_param_list(const Scripting_Object &so)
{
  std::vector<Scripting_Object> sos;
  sos.push_back(so);
  return sos;
}
std::vector<Scripting_Object> build_param_list(const Scripting_Object &so1, const Scripting_Object &so2)
{
  std::vector<Scripting_Object> sos;
  sos.push_back(so1);
  sos.push_back(so2);
  return sos;
}
std::vector<Scripting_Object> build_param_list(const Scripting_Object &so1,  const Scripting_Object &so2, const Scripting_Object &so3)
{
  std::vector<Scripting_Object> sos;
  sos.push_back(so1);
  sos.push_back(so2);
  sos.push_back(so3);
  return sos;
}

#endif

<commit_msg>Add support for getting command argument types as a vector<commit_after>#ifndef __scripting_function_hpp__
#define __scripting_function_hpp__

#include "scripting_object.hpp"
#include "scripting_type_info.hpp"
#include <string>
#include <boost/function.hpp>
#include <boost/bind.hpp>
#include <stdexcept>
#include <vector>

template<typename Ret>
std::vector<Type_Info> build_param_type_list(const boost::function<Ret ()> &f)
{
  return std::vector<Type_Info>();
}

template<typename Ret, typename Param1>
std::vector<Type_Info> build_param_type_list(const boost::function<Ret (Param1)> &f)
{
  std::vector<Type_Info> ti;
  ti.push_back(Get_Type_Info<Param1>()());
  return ti;
}

template<typename Ret, typename Param1, typename Param2>
std::vector<Type_Info> build_param_type_list(const boost::function<Ret (Param1, Param2)> &f)
{
  std::vector<Type_Info> ti;
  ti.push_back(Get_Type_Info<Param1>()());
  ti.push_back(Get_Type_Info<Param2>()());
  return ti;
}

// handle_return implementations
template<typename Ret>
struct Handle_Return
{
  Scripting_Object operator()(const boost::function<Ret ()> &f)
  {
    return Scripting_Object(f());
  }
};

template<typename Ret>
struct Handle_Return<Ret &>
{
  Scripting_Object operator()(const boost::function<Ret &()> &f)
  {
    return Scripting_Object(boost::ref(f()));
  }
};

template<>
struct Handle_Return<void>
{
  Scripting_Object operator()(const boost::function<void ()> &f)
  {
    f();
    return Scripting_Object();
  }
};

// call_func implementations todo: handle reference return types
// to be made variadic
template<typename Ret, typename Param1, typename Param2>
Scripting_Object call_func(const boost::function<Ret (Param1, Param2)> &f, const std::vector<Scripting_Object> &params)
{
  if (params.size() != 2)
  {
    throw std::range_error("Incorrect number of parameters");
  } else {
    return Handle_Return<Ret>()(boost::bind(f, Cast_Helper<Param1>()(params[0]), Cast_Helper<Param2>()(params[1])));
  }
}

template<typename Ret, typename Param1>
Scripting_Object call_func(const boost::function<Ret (Param1)> &f, const std::vector<Scripting_Object> &params)
{
  if (params.size() != 1)
  {
    throw std::range_error("Incorrect number of parameters");
  } else {
    return Handle_Return<Ret>()(boost::bind(f, Cast_Helper<Param1>()(params[0])));
  }
}

template<typename Ret>
Scripting_Object call_func(const boost::function<Ret ()> &f, const std::vector<Scripting_Object> &params)
{
  if (params.size() != 0)
  {
    throw std::range_error("Incorrect number of parameters");
  } else {
    return Handle_Return<Ret>()(f);
  }
}

class Function_Handler
{
  public:
    virtual Scripting_Object operator()(const std::vector<Scripting_Object> &params) = 0;
    virtual std::vector<Type_Info> get_param_types() = 0;

};

template<typename Func>
class Function_Handler_Impl : public Function_Handler
{
  public:
    Function_Handler_Impl(const Func &f)
      : m_f(f)
    {
    }

    virtual Scripting_Object operator()(const std::vector<Scripting_Object> &params)
    {
      return call_func(m_f, params);
    }

    virtual std::vector<Type_Info> get_param_types()
    {
      return build_param_type_list(m_f);
    }

  private:
    Func m_f;
};

std::vector<Scripting_Object> build_param_list(const Scripting_Object &so)
{
  std::vector<Scripting_Object> sos;
  sos.push_back(so);
  return sos;
}
std::vector<Scripting_Object> build_param_list(const Scripting_Object &so1, const Scripting_Object &so2)
{
  std::vector<Scripting_Object> sos;
  sos.push_back(so1);
  sos.push_back(so2);
  return sos;
}
std::vector<Scripting_Object> build_param_list(const Scripting_Object &so1,  const Scripting_Object &so2, const Scripting_Object &so3)
{
  std::vector<Scripting_Object> sos;
  sos.push_back(so1);
  sos.push_back(so2);
  sos.push_back(so3);
  return sos;
}

#endif

<|endoftext|>


<commit_before>//
// Copyright (c) 2008-2013 the Urho3D project.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//

#include "Precompiled.h"
#include "Context.h"
#include "InputEvents.h"
#include "Log.h"
#include "Slider.h"
#include "UIEvents.h"

#include "DebugNew.h"

namespace Urho3D
{

const char* orientations[] =
{
    "Horizontal",
    "Vertical",
    0
};

template<> Orientation Variant::Get<Orientation>() const
{
    return (Orientation)GetInt();
}

extern const char* UI_CATEGORY;

Slider::Slider(Context* context) :
    BorderImage(context),
    orientation_(O_HORIZONTAL),
    range_(1.0f),
    value_(0.0f),
    dragSlider_(false),
    repeatRate_(0.0f)
{
    enabled_ = true;
    knob_ = CreateChild<BorderImage>("S_Knob");
    knob_->SetInternal(true);

    UpdateSlider();
}

Slider::~Slider()
{
}

void Slider::RegisterObject(Context* context)
{
    context->RegisterFactory<Slider>(UI_CATEGORY);

    COPY_BASE_ATTRIBUTES(Slider, BorderImage);
    UPDATE_ATTRIBUTE_DEFAULT_VALUE(Slider, "Is Enabled", true);
    ENUM_ACCESSOR_ATTRIBUTE(Slider, "Orientation", GetOrientation, SetOrientation, Orientation, orientations, O_HORIZONTAL, AM_FILE);
    ACCESSOR_ATTRIBUTE(Slider, VAR_FLOAT, "Range", GetRange, SetRange, float, 1.0f, AM_FILE);
    ACCESSOR_ATTRIBUTE(Slider, VAR_FLOAT, "Value", GetValue, SetValue, float, 0.0f, AM_FILE);
    ACCESSOR_ATTRIBUTE(Slider, VAR_FLOAT, "Repeat Rate", GetRepeatRate, SetRepeatRate, float, 0.0f, AM_FILE);
}

void Slider::Update(float timeStep)
{
    if (dragSlider_)
        hovering_ = true;

    // Propagate hover effect to the slider knob
    knob_->SetHovering(hovering_);
    knob_->SetSelected(hovering_);
}

void Slider::OnHover(const IntVector2& position, const IntVector2& screenPosition, int buttons, int qualifiers, Cursor* cursor)
{
    BorderImage::OnHover(position, screenPosition, buttons, qualifiers, cursor);

    // Show hover effect if inside the slider knob
    hovering_ = knob_->IsInside(screenPosition, true);

    // If not hovering on the knob, send it as page event
    if (!hovering_)
        Page(position, 0, buttons, qualifiers);
}

void Slider::OnClickBegin(const IntVector2& position, const IntVector2& screenPosition, int button, int buttons, int qualifiers, Cursor* cursor)
{
    selected_ = true;
    hovering_ = knob_->IsInside(screenPosition, true);
    if (!hovering_)
        Page(position, button, buttons, qualifiers);
}

void Slider::OnDragBegin(const IntVector2& position, const IntVector2& screenPosition, int buttons, int qualifiers, Cursor* cursor)
{
    dragBeginCursor_ = position;
    dragBeginPosition_ = knob_->GetPosition();
    dragSlider_ = knob_->IsInside(screenPosition, true);
}

void Slider::OnDragMove(const IntVector2& position, const IntVector2& screenPosition, int buttons, int qualifiers, Cursor* cursor)
{
    if (!dragSlider_ || GetSize() == knob_->GetSize())
        return;

    float newValue = value_;
    IntVector2 delta = position - dragBeginCursor_;

    if (orientation_ == O_HORIZONTAL)
    {
        int newX = Clamp(dragBeginPosition_.x_ + delta.x_, 0, GetWidth() - knob_->GetWidth());
        knob_->SetPosition(newX, 0);
        newValue = (float)newX * range_ / (float)(GetWidth() - knob_->GetWidth());
    }
    else
    {
        int newY = Clamp(dragBeginPosition_.y_ + delta.y_, 0, GetHeight() - knob_->GetHeight());
        knob_->SetPosition(0, newY);
        newValue = (float)newY * range_ / (float)(GetHeight() - knob_->GetHeight());
    }

    SetValue(newValue);
}

void Slider::OnDragEnd(const IntVector2& position, const IntVector2& screenPosition, Cursor* cursor)
{
    dragSlider_ = false;
    selected_ = false;
}

void Slider::OnResize()
{
    UpdateSlider();
}

void Slider::SetOrientation(Orientation type)
{
    orientation_ = type;
    UpdateSlider();
}

void Slider::SetRange(float range)
{
    range = Max(range, 0.0f);
    if (range != range_)
    {
        range_ = range;
        UpdateSlider();
    }
}

void Slider::SetValue(float value)
{
    value = Clamp(value, 0.0f, range_);
    if (value != value_)
    {
        value_ = value;
        UpdateSlider();

        using namespace SliderChanged;

        VariantMap eventData;
        eventData[P_ELEMENT] = (void*)this;
        eventData[P_VALUE] = value_;
        SendEvent(E_SLIDERCHANGED, eventData);
    }
}

void Slider::ChangeValue(float delta)
{
    SetValue(value_ + delta);
}

void Slider::SetRepeatRate(float rate)
{
    repeatRate_ = Max(rate, 0.0f);
}

bool Slider::FilterImplicitAttributes(XMLElement& dest) const
{
    if (!BorderImage::FilterImplicitAttributes(dest))
        return false;

    XMLElement childElem = dest.GetChild("element");
    if (!childElem)
        return false;
    if (!RemoveChildXML(childElem, "Name", "S_Knob"))
        return false;
    if (!RemoveChildXML(childElem, "Position"))
        return false;
    if (!RemoveChildXML(childElem, "Size"))
        return false;

    return true;
}

void Slider::UpdateSlider()
{
    const IntRect& border = knob_->GetBorder();

    if (range_ > 0.0f)
    {
        if (orientation_ == O_HORIZONTAL)
        {
            int sliderLength = (int)Max((float)GetWidth() / (range_ + 1.0f), (float)(border.left_ + border.right_));
            float sliderPos = (float)(GetWidth() - sliderLength) * value_ / range_;
            knob_->SetSize(sliderLength, GetHeight());
            knob_->SetPosition(Clamp((int)(sliderPos + 0.5f), 0, GetWidth() - knob_->GetWidth()), 0);
        }
        else
        {
            int sliderLength = (int)Max((float)GetHeight() / (range_ + 1.0f), (float)(border.top_ + border.bottom_));
            float sliderPos = (float)(GetHeight() - sliderLength) * value_ / range_;
            knob_->SetSize(GetWidth(), sliderLength);
            knob_->SetPosition(0, Clamp((int)(sliderPos + 0.5f), 0, GetHeight() - knob_->GetHeight()));
        }
    }
    else
    {
        knob_->SetSize(GetSize());
        knob_->SetPosition(0, 0);
    }
}

void Slider::Page(const IntVector2& position, int button, int buttons, int qualifiers)
{
    IntVector2 offsetXY = position - knob_->GetPosition() - knob_->GetSize() / 2;
    int offset = orientation_ == O_HORIZONTAL ? offsetXY.x_ : offsetXY.y_;
    float length = (float)(orientation_ == O_HORIZONTAL ? GetWidth() : GetHeight());

    using namespace SliderPaged;

    VariantMap eventData;
    eventData[P_ELEMENT] = (void*)this;
    eventData[P_OFFSET] = offset;
    // Only generate the 'click' variant of the event when the slider is selected
    // i.e. when it has received the first initial click.
    if (selected_ && repeatRate_ > 0.0f && repeatTimer_.GetMSec(false) >= Lerp(1000.0f / repeatRate_, 0, Abs(offset) / length))
    {
        repeatTimer_.Reset();
        eventData[P_BUTTON] = button;
        eventData[P_BUTTONS] = buttons;
        eventData[P_QUALIFIERS] = qualifiers;
    }
    // When without buttons & qualifiers parameters, the receiver should interpret
    // this event as just mouse hovering on slider's 'paging' area instead
    SendEvent(E_SLIDERPAGED, eventData);
}

}
<commit_msg>Fixed repeat paging of the slider.<commit_after>//
// Copyright (c) 2008-2013 the Urho3D project.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//

#include "Precompiled.h"
#include "Context.h"
#include "InputEvents.h"
#include "Log.h"
#include "Slider.h"
#include "UIEvents.h"

#include "DebugNew.h"

namespace Urho3D
{

const char* orientations[] =
{
    "Horizontal",
    "Vertical",
    0
};

template<> Orientation Variant::Get<Orientation>() const
{
    return (Orientation)GetInt();
}

extern const char* UI_CATEGORY;

Slider::Slider(Context* context) :
    BorderImage(context),
    orientation_(O_HORIZONTAL),
    range_(1.0f),
    value_(0.0f),
    dragSlider_(false),
    repeatRate_(0.0f)
{
    enabled_ = true;
    knob_ = CreateChild<BorderImage>("S_Knob");
    knob_->SetInternal(true);

    UpdateSlider();
}

Slider::~Slider()
{
}

void Slider::RegisterObject(Context* context)
{
    context->RegisterFactory<Slider>(UI_CATEGORY);

    COPY_BASE_ATTRIBUTES(Slider, BorderImage);
    UPDATE_ATTRIBUTE_DEFAULT_VALUE(Slider, "Is Enabled", true);
    ENUM_ACCESSOR_ATTRIBUTE(Slider, "Orientation", GetOrientation, SetOrientation, Orientation, orientations, O_HORIZONTAL, AM_FILE);
    ACCESSOR_ATTRIBUTE(Slider, VAR_FLOAT, "Range", GetRange, SetRange, float, 1.0f, AM_FILE);
    ACCESSOR_ATTRIBUTE(Slider, VAR_FLOAT, "Value", GetValue, SetValue, float, 0.0f, AM_FILE);
    ACCESSOR_ATTRIBUTE(Slider, VAR_FLOAT, "Repeat Rate", GetRepeatRate, SetRepeatRate, float, 0.0f, AM_FILE);
}

void Slider::Update(float timeStep)
{
    if (dragSlider_)
        hovering_ = true;

    // Propagate hover effect to the slider knob
    knob_->SetHovering(hovering_);
    knob_->SetSelected(hovering_);
}

void Slider::OnHover(const IntVector2& position, const IntVector2& screenPosition, int buttons, int qualifiers, Cursor* cursor)
{
    BorderImage::OnHover(position, screenPosition, buttons, qualifiers, cursor);

    // Show hover effect if inside the slider knob
    hovering_ = knob_->IsInside(screenPosition, true);

    // If not hovering on the knob, send it as page event
    if (!hovering_)
        Page(position, 0, buttons, qualifiers);
}

void Slider::OnClickBegin(const IntVector2& position, const IntVector2& screenPosition, int button, int buttons, int qualifiers, Cursor* cursor)
{
    selected_ = true;
    hovering_ = knob_->IsInside(screenPosition, true);
    if (!hovering_)
        Page(position, button, buttons, qualifiers);
}

void Slider::OnDragBegin(const IntVector2& position, const IntVector2& screenPosition, int buttons, int qualifiers, Cursor* cursor)
{
    dragBeginCursor_ = position;
    dragBeginPosition_ = knob_->GetPosition();
    dragSlider_ = knob_->IsInside(screenPosition, true);
}

void Slider::OnDragMove(const IntVector2& position, const IntVector2& screenPosition, int buttons, int qualifiers, Cursor* cursor)
{
    if (!dragSlider_ || GetSize() == knob_->GetSize())
        return;

    float newValue = value_;
    IntVector2 delta = position - dragBeginCursor_;

    if (orientation_ == O_HORIZONTAL)
    {
        int newX = Clamp(dragBeginPosition_.x_ + delta.x_, 0, GetWidth() - knob_->GetWidth());
        knob_->SetPosition(newX, 0);
        newValue = (float)newX * range_ / (float)(GetWidth() - knob_->GetWidth());
    }
    else
    {
        int newY = Clamp(dragBeginPosition_.y_ + delta.y_, 0, GetHeight() - knob_->GetHeight());
        knob_->SetPosition(0, newY);
        newValue = (float)newY * range_ / (float)(GetHeight() - knob_->GetHeight());
    }

    SetValue(newValue);
}

void Slider::OnDragEnd(const IntVector2& position, const IntVector2& screenPosition, Cursor* cursor)
{
    dragSlider_ = false;
    selected_ = false;
}

void Slider::OnResize()
{
    UpdateSlider();
}

void Slider::SetOrientation(Orientation type)
{
    orientation_ = type;
    UpdateSlider();
}

void Slider::SetRange(float range)
{
    range = Max(range, 0.0f);
    if (range != range_)
    {
        range_ = range;
        UpdateSlider();
    }
}

void Slider::SetValue(float value)
{
    value = Clamp(value, 0.0f, range_);
    if (value != value_)
    {
        value_ = value;
        UpdateSlider();

        using namespace SliderChanged;

        VariantMap eventData;
        eventData[P_ELEMENT] = (void*)this;
        eventData[P_VALUE] = value_;
        SendEvent(E_SLIDERCHANGED, eventData);
    }
}

void Slider::ChangeValue(float delta)
{
    SetValue(value_ + delta);
}

void Slider::SetRepeatRate(float rate)
{
    repeatRate_ = Max(rate, 0.0f);
}

bool Slider::FilterImplicitAttributes(XMLElement& dest) const
{
    if (!BorderImage::FilterImplicitAttributes(dest))
        return false;

    XMLElement childElem = dest.GetChild("element");
    if (!childElem)
        return false;
    if (!RemoveChildXML(childElem, "Name", "S_Knob"))
        return false;
    if (!RemoveChildXML(childElem, "Position"))
        return false;
    if (!RemoveChildXML(childElem, "Size"))
        return false;

    return true;
}

void Slider::UpdateSlider()
{
    const IntRect& border = knob_->GetBorder();

    if (range_ > 0.0f)
    {
        if (orientation_ == O_HORIZONTAL)
        {
            int sliderLength = (int)Max((float)GetWidth() / (range_ + 1.0f), (float)(border.left_ + border.right_));
            float sliderPos = (float)(GetWidth() - sliderLength) * value_ / range_;
            knob_->SetSize(sliderLength, GetHeight());
            knob_->SetPosition(Clamp((int)(sliderPos + 0.5f), 0, GetWidth() - knob_->GetWidth()), 0);
        }
        else
        {
            int sliderLength = (int)Max((float)GetHeight() / (range_ + 1.0f), (float)(border.top_ + border.bottom_));
            float sliderPos = (float)(GetHeight() - sliderLength) * value_ / range_;
            knob_->SetSize(GetWidth(), sliderLength);
            knob_->SetPosition(0, Clamp((int)(sliderPos + 0.5f), 0, GetHeight() - knob_->GetHeight()));
        }
    }
    else
    {
        knob_->SetSize(GetSize());
        knob_->SetPosition(0, 0);
    }
}

void Slider::Page(const IntVector2& position, int button, int buttons, int qualifiers)
{
    IntVector2 offsetXY = position - knob_->GetPosition() - knob_->GetSize() / 2;
    int offset = orientation_ == O_HORIZONTAL ? offsetXY.x_ : offsetXY.y_;
    float length = (float)(orientation_ == O_HORIZONTAL ? GetWidth() : GetHeight());

    using namespace SliderPaged;

    // If button is 0 (passed from hover event) assume it's the lowest pressed bit in buttons
    if (!button && buttons)
    {
        if (buttons & MOUSEB_LEFT)
            button = MOUSEB_LEFT;
        else if (buttons & MOUSEB_MIDDLE)
            button = MOUSEB_MIDDLE;
        else if (buttons & MOUSEB_RIGHT)
            button = MOUSEB_RIGHT;
    }

    VariantMap eventData;
    eventData[P_ELEMENT] = (void*)this;
    eventData[P_OFFSET] = offset;
    // Only generate the 'click' variant of the event when the slider is selected
    // i.e. when it has received the first initial click.
    if (selected_ && repeatRate_ > 0.0f && repeatTimer_.GetMSec(false) >= Lerp(1000.0f / repeatRate_, 0, Abs(offset) / length))
    {
        repeatTimer_.Reset();
        eventData[P_BUTTON] = button;
        eventData[P_BUTTONS] = buttons;
        eventData[P_QUALIFIERS] = qualifiers;
    }
    // When without buttons & qualifiers parameters, the receiver should interpret
    // this event as just mouse hovering on slider's 'paging' area instead
    SendEvent(E_SLIDERPAGED, eventData);
}

}
<|endoftext|>


<commit_before>#pragma once
/**
	@file
	@brief zlib compressor and decompressor class

	Copyright (C) 2009 Cybozu Labs, Inc., all rights reserved.
*/

#include <cybozu/exception.hpp>
#include <cybozu/endian.hpp>
#include <cybozu/stream_fwd.hpp>
#include <assert.h>
#include <stdio.h>
#include <zlib.h>

#ifdef _MSC_VER
	#ifdef _DLL_CPPLIB
		#pragma comment(lib, "zlib_md.lib")
	#else
		#pragma comment(lib, "zlib_mt.lib")
	#endif
#endif

namespace cybozu {

namespace zlib_local {

const int DEF_MEM_LEVEL = 8;

} // zlib_local

/**
	zlib compressor class
	OutputStream must have size_t write(const char *buf, size_t size);
*/
template<class OutputStream, size_t maxBufSize = 2048>
class ZlibCompressorT {
	OutputStream& os_;
	unsigned int crc_;
	unsigned int totalSize_; /* mod 2^32 */
	z_stream z_;
	char buf_[maxBufSize];
	bool isFlushCalled_;
	const bool useGzip_;
	ZlibCompressorT(const ZlibCompressorT&);
	void operator=(const ZlibCompressorT&);
public:
	/**
		@param os [in] output stream
		@param useGzip [in] useGzip if true, use deflate if false
		@note useGzip option is not fully tested, so default off
	*/
	ZlibCompressorT(OutputStream& os, bool useGzip = false, int compressionLevel = Z_DEFAULT_COMPRESSION)
		: os_(os)
		, crc_(crc32(0L, Z_NULL, 0))
		, totalSize_(0)
		, isFlushCalled_(false)
		, useGzip_(useGzip)
	{
		z_.zalloc = Z_NULL;
		z_.zfree = Z_NULL;
		z_.opaque = Z_NULL;
		if (useGzip_) {
			if (deflateInit2(&z_, compressionLevel, Z_DEFLATED, -MAX_WBITS, zlib_local::DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY) != Z_OK) {
				throw cybozu::Exception("zlib:ZlibCompressorT:deflateInit2") << std::string(z_.msg);
			}
			char header[] = "\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\x03"; /* OS_CODE = 0x03(Unix) */
			write_os(header, 10);
		} else {
			if (deflateInit(&z_, compressionLevel) != Z_OK) {
				throw cybozu::Exception("zlib:ZlibCompressorT:deflateInit") << std::string(z_.msg);
			}
		}
	}
	~ZlibCompressorT()
	{
		if (!isFlushCalled_) {
			try {
				flush();
			} catch (std::exception& e) {
				fprintf(stderr, "zlib:ZlibCompressor:flush:exception:%s\n", e.what());
			} catch (...) {
				fprintf(stderr, "zlib:ZlibCompressor:flush:unknown exception\n");
			}
		}
		deflateEnd(&z_);
	}
	/*
		compress buf
		@param buf [in] input data
		@param size [in] input data size
	*/
	void write(const void *buf, size_t _size)
	{
		if (_size >= (1u << 31)) throw cybozu::Exception("zlib:ZlibCompressor:write:too large size") << _size;
		uint32_t size = (uint32_t)_size;
		if (useGzip_) {
			crc_ = crc32(crc_, (const Bytef *)buf, size);
			totalSize_ += (unsigned int)size;
		}
		z_.next_in = (Bytef*)const_cast<char*>((const char*)buf);
		z_.avail_in = size;
		while (z_.avail_in > 0) {
			z_.next_out = (Bytef*)buf_;
			z_.avail_out = maxBufSize;

			int ret = deflate(&z_, Z_NO_FLUSH);
			if (ret != Z_STREAM_END && ret != Z_OK) {
				throw cybozu::Exception("zlib:ZlibCompressor:exec:compress") << std::string(z_.msg);
			}
			write_os(buf_, maxBufSize - z_.avail_out);
			if (ret == Z_STREAM_END) break;
		}
	}
	void flush()
	{
		isFlushCalled_ = true;
		z_.next_in = 0;
		z_.avail_in = 0;

		for (;;) {
			z_.next_out = (Bytef*)buf_;
			z_.avail_out = maxBufSize;

			int ret = deflate(&z_, Z_FINISH);
			if (ret != Z_STREAM_END && ret != Z_OK) {
				throw cybozu::Exception("zlib:ZlibCompressor:flush") << std::string(z_.msg);
			}
			write_os(buf_, sizeof(buf_) - z_.avail_out);
			if (ret == Z_STREAM_END) break;
		}
		if (useGzip_) {
			char tail[8];
			cybozu::Set32bitAsLE(&tail[0], crc_);
			cybozu::Set32bitAsLE(&tail[4], totalSize_);
			write_os(tail, sizeof(tail));
		}
	}
private:
	void write_os(const char *buf, size_t size)
	{
		cybozu::OutputStreamTag<OutputStream>::write(os_, buf, size);
	}
};

/**
	zlib decompressor class
	InputStream must have size_t read(char *str, size_t size);
*/
template<class InputStream, size_t maxBufSize = 2048>
class ZlibDecompressorT {
	typedef cybozu::InputStreamTag<InputStream> In;
	InputStream& is_;
	unsigned int crc_;
	unsigned int totalSize_; /* mod 2^32 */
	z_stream z_;
	int ret_;
	char buf_[maxBufSize];
	const bool useGzip_;
	bool readGzipHeader_;
	void readAll(char *buf, size_t size)
	{
		In::read(is_, buf, size);
	}
	void skipToZero()
	{
		for (;;) {
			char buf[1];
			readAll(buf, 1);
			if (buf[0] == '\0') break;
		}
	}
	void skip(int size)
	{
		for (int i = 0 ; i < size; i++) {
			char buf[1];
			readAll(buf, 1);
		}
	}
	void readGzipHeader()
	{
		char header[10];
		readAll(header, sizeof(header));
		enum {
			FHCRC = 1 << 1,
			FEXTRA = 1 << 2,
			FNAME = 1 << 3,
			FCOMMENT = 1 << 4,
			RESERVED = 7 << 5,
		};
		char flg = header[3];
		if (header[0] == '\x1f'
			&& header[1] == '\x8b'
			&& header[2] == Z_DEFLATED
			&& !(flg & RESERVED)) {
			if (flg & FEXTRA) {
				char xlen[2];
				readAll(xlen, sizeof(xlen));
				int size = cybozu::Get16bitAsLE(xlen);
				skip(size);
			}
			if (flg & FNAME) {
				skipToZero();
			}
			if (flg & FCOMMENT) {
				skipToZero();
			}
			if (flg & FHCRC) {
				skip(2);
			}
			return;
		}
		throw cybozu::Exception("zlib:ZlibDecompressorT:readGzipHeader:bad gzip header") << std::string(header, 10);
	}
	ZlibDecompressorT(const ZlibDecompressorT&);
	void operator=(const ZlibDecompressorT&);
public:
	/**
		@param os [in] input stream
		@param useGzip [in] useGzip if true, use deflate if false
		@note useGzip option is not fully tested, so default off
	*/
	ZlibDecompressorT(InputStream& is, bool useGzip = false)
		: is_(is)
		, crc_(crc32(0L, Z_NULL, 0))
		, totalSize_(0)
		, ret_(Z_OK)
		, useGzip_(useGzip)
		, readGzipHeader_(false)
	{
		z_.zalloc = Z_NULL;
		z_.zfree = Z_NULL;
		z_.opaque = Z_NULL;
		z_.next_in = 0;
		z_.avail_in = 0;
		if (useGzip_) {
			if (inflateInit2(&z_, -MAX_WBITS) != Z_OK) {
				throw cybozu::Exception("zlib:ZlibDecompressorT:inflateInit2") << std::string(z_.msg);
			}
		} else {
			if (inflateInit(&z_) != Z_OK) {
				throw cybozu::Exception("zlib:ZlibDecompressorT:inflateInit") << std::string(z_.msg);
			}
		}
	}
	~ZlibDecompressorT()
	{
		inflateEnd(&z_);
	}
	/*
		decompress is
		@param str [out] decompressed data
		@param str [out] max buf size
		@return read size
	*/
	size_t readSome(void *buf, size_t _size)
	{
		if (_size == 0) return 0;
		if (_size >= (1u << 31)) throw cybozu::Exception("zlib:ZlibDecompressorT:readSome:too large size") << _size;
		uint32_t size = (uint32_t)_size;
		if (useGzip_ && !readGzipHeader_) {
			readGzipHeader();
			readGzipHeader_ = true;
		}
		z_.next_out = (Bytef*)buf;
		z_.avail_out = size;
		do {
			if (z_.avail_in == 0) {
				z_.avail_in = (uint32_t)In::readSome(is_, buf_, maxBufSize);
				if (ret_ == Z_STREAM_END && z_.avail_in == 0) return 0;
				z_.next_in = (Bytef*)buf_;
			}
			ret_ = inflate(&z_, Z_NO_FLUSH);
			if (ret_ == Z_STREAM_END) break;
			if (ret_ != Z_OK) {
				throw cybozu::Exception("zlib:ZlibDecompressorT:readSome:inflate") << std::string(z_.msg);
			}
		} while (size == z_.avail_out);

		return size - z_.avail_out;
	}
	void read(void *buf, size_t size)
	{
		char *p = (char *)buf;
		while (size > 0) {
			size_t readSize = readSome(p, size);
			if (readSize == 0) throw cybozu::Exception("zlib:ZlibDecompressorT:read");
			p += readSize;
			size -= readSize;
		}
	}
};

} // cybozu

<commit_msg>add isEmpty() to verify whether data is empty or not<commit_after>#pragma once
/**
	@file
	@brief zlib compressor and decompressor class

	Copyright (C) 2009 Cybozu Labs, Inc., all rights reserved.
*/

#include <cybozu/exception.hpp>
#include <cybozu/endian.hpp>
#include <cybozu/stream_fwd.hpp>
#include <assert.h>
#include <stdio.h>
#include <zlib.h>

#ifdef _MSC_VER
	#ifdef _DLL_CPPLIB
		#pragma comment(lib, "zlib_md.lib")
	#else
		#pragma comment(lib, "zlib_mt.lib")
	#endif
#endif

namespace cybozu {

namespace zlib_local {

const int DEF_MEM_LEVEL = 8;

} // zlib_local

/**
	zlib compressor class
	OutputStream must have size_t write(const char *buf, size_t size);
*/
template<class OutputStream, size_t maxBufSize = 2048>
class ZlibCompressorT {
	OutputStream& os_;
	unsigned int crc_;
	unsigned int totalSize_; /* mod 2^32 */
	z_stream z_;
	char buf_[maxBufSize];
	bool isFlushCalled_;
	const bool useGzip_;
	ZlibCompressorT(const ZlibCompressorT&);
	void operator=(const ZlibCompressorT&);
public:
	/**
		@param os [in] output stream
		@param useGzip [in] useGzip if true, use deflate if false
		@note useGzip option is not fully tested, so default off
	*/
	ZlibCompressorT(OutputStream& os, bool useGzip = false, int compressionLevel = Z_DEFAULT_COMPRESSION)
		: os_(os)
		, crc_(crc32(0L, Z_NULL, 0))
		, totalSize_(0)
		, isFlushCalled_(false)
		, useGzip_(useGzip)
	{
		z_.zalloc = Z_NULL;
		z_.zfree = Z_NULL;
		z_.opaque = Z_NULL;
		if (useGzip_) {
			if (deflateInit2(&z_, compressionLevel, Z_DEFLATED, -MAX_WBITS, zlib_local::DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY) != Z_OK) {
				throw cybozu::Exception("zlib:ZlibCompressorT:deflateInit2") << std::string(z_.msg);
			}
			char header[] = "\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\x03"; /* OS_CODE = 0x03(Unix) */
			write_os(header, 10);
		} else {
			if (deflateInit(&z_, compressionLevel) != Z_OK) {
				throw cybozu::Exception("zlib:ZlibCompressorT:deflateInit") << std::string(z_.msg);
			}
		}
	}
	~ZlibCompressorT()
	{
		if (!isFlushCalled_) {
			try {
				flush();
			} catch (std::exception& e) {
				fprintf(stderr, "zlib:ZlibCompressor:flush:exception:%s\n", e.what());
			} catch (...) {
				fprintf(stderr, "zlib:ZlibCompressor:flush:unknown exception\n");
			}
		}
		deflateEnd(&z_);
	}
	/*
		compress buf
		@param buf [in] input data
		@param size [in] input data size
	*/
	void write(const void *buf, size_t _size)
	{
		if (_size >= (1u << 31)) throw cybozu::Exception("zlib:ZlibCompressor:write:too large size") << _size;
		uint32_t size = (uint32_t)_size;
		if (useGzip_) {
			crc_ = crc32(crc_, (const Bytef *)buf, size);
			totalSize_ += (unsigned int)size;
		}
		z_.next_in = (Bytef*)const_cast<char*>((const char*)buf);
		z_.avail_in = size;
		while (z_.avail_in > 0) {
			z_.next_out = (Bytef*)buf_;
			z_.avail_out = maxBufSize;

			int ret = deflate(&z_, Z_NO_FLUSH);
			if (ret != Z_STREAM_END && ret != Z_OK) {
				throw cybozu::Exception("zlib:ZlibCompressor:exec:compress") << std::string(z_.msg);
			}
			write_os(buf_, maxBufSize - z_.avail_out);
			if (ret == Z_STREAM_END) break;
		}
	}
	void flush()
	{
		isFlushCalled_ = true;
		z_.next_in = 0;
		z_.avail_in = 0;

		for (;;) {
			z_.next_out = (Bytef*)buf_;
			z_.avail_out = maxBufSize;

			int ret = deflate(&z_, Z_FINISH);
			if (ret != Z_STREAM_END && ret != Z_OK) {
				throw cybozu::Exception("zlib:ZlibCompressor:flush") << std::string(z_.msg);
			}
			write_os(buf_, sizeof(buf_) - z_.avail_out);
			if (ret == Z_STREAM_END) break;
		}
		if (useGzip_) {
			char tail[8];
			cybozu::Set32bitAsLE(&tail[0], crc_);
			cybozu::Set32bitAsLE(&tail[4], totalSize_);
			write_os(tail, sizeof(tail));
		}
	}
private:
	void write_os(const char *buf, size_t size)
	{
		cybozu::OutputStreamTag<OutputStream>::write(os_, buf, size);
	}
};

/**
	zlib decompressor class
	InputStream must have size_t read(char *str, size_t size);
*/
template<class InputStream, size_t maxBufSize = 2048>
class ZlibDecompressorT {
	typedef cybozu::InputStreamTag<InputStream> In;
	InputStream& is_;
	unsigned int crc_;
	unsigned int totalSize_; /* mod 2^32 */
	z_stream z_;
	int ret_;
	char buf_[maxBufSize];
	const bool useGzip_;
	bool readGzipHeader_;
	void readAll(char *buf, size_t size)
	{
		In::read(is_, buf, size);
	}
	void skipToZero()
	{
		for (;;) {
			char buf[1];
			readAll(buf, 1);
			if (buf[0] == '\0') break;
		}
	}
	void skip(int size)
	{
		for (int i = 0 ; i < size; i++) {
			char buf[1];
			readAll(buf, 1);
		}
	}
	void readGzipHeader()
	{
		char header[10];
		readAll(header, sizeof(header));
		enum {
			FHCRC = 1 << 1,
			FEXTRA = 1 << 2,
			FNAME = 1 << 3,
			FCOMMENT = 1 << 4,
			RESERVED = 7 << 5,
		};
		char flg = header[3];
		if (header[0] == '\x1f'
			&& header[1] == '\x8b'
			&& header[2] == Z_DEFLATED
			&& !(flg & RESERVED)) {
			if (flg & FEXTRA) {
				char xlen[2];
				readAll(xlen, sizeof(xlen));
				int size = cybozu::Get16bitAsLE(xlen);
				skip(size);
			}
			if (flg & FNAME) {
				skipToZero();
			}
			if (flg & FCOMMENT) {
				skipToZero();
			}
			if (flg & FHCRC) {
				skip(2);
			}
			return;
		}
		throw cybozu::Exception("zlib:ZlibDecompressorT:readGzipHeader:bad gzip header") << std::string(header, 10);
	}
	ZlibDecompressorT(const ZlibDecompressorT&);
	void operator=(const ZlibDecompressorT&);
public:
	/**
		@param os [in] input stream
		@param useGzip [in] useGzip if true, use deflate if false
		@note useGzip option is not fully tested, so default off
	*/
	ZlibDecompressorT(InputStream& is, bool useGzip = false)
		: is_(is)
		, crc_(crc32(0L, Z_NULL, 0))
		, totalSize_(0)
		, ret_(Z_OK)
		, useGzip_(useGzip)
		, readGzipHeader_(false)
	{
		z_.zalloc = Z_NULL;
		z_.zfree = Z_NULL;
		z_.opaque = Z_NULL;
		z_.next_in = 0;
		z_.avail_in = 0;
		if (useGzip_) {
			if (inflateInit2(&z_, -MAX_WBITS) != Z_OK) {
				throw cybozu::Exception("zlib:ZlibDecompressorT:inflateInit2") << std::string(z_.msg);
			}
		} else {
			if (inflateInit(&z_) != Z_OK) {
				throw cybozu::Exception("zlib:ZlibDecompressorT:inflateInit") << std::string(z_.msg);
			}
		}
	}
	~ZlibDecompressorT()
	{
		inflateEnd(&z_);
	}
	/*
		decompress is
		@param str [out] decompressed data
		@param str [out] max buf size
		@return read size
	*/
	size_t readSome(void *buf, size_t _size)
	{
		if (_size == 0) return 0;
		if (_size >= (1u << 31)) throw cybozu::Exception("zlib:ZlibDecompressorT:readSome:too large size") << _size;
		uint32_t size = (uint32_t)_size;
		if (useGzip_ && !readGzipHeader_) {
			readGzipHeader();
			readGzipHeader_ = true;
		}
		z_.next_out = (Bytef*)buf;
		z_.avail_out = size;
		do {
			if (z_.avail_in == 0) {
				z_.avail_in = (uint32_t)In::readSome(is_, buf_, maxBufSize);
				if (ret_ == Z_STREAM_END && z_.avail_in == 0) return 0;
				z_.next_in = (Bytef*)buf_;
			}
			ret_ = inflate(&z_, Z_NO_FLUSH);
			if (ret_ == Z_STREAM_END) break;
			if (ret_ != Z_OK) {
				throw cybozu::Exception("zlib:ZlibDecompressorT:readSome:inflate") << std::string(z_.msg);
			}
		} while (size == z_.avail_out);

		return size - z_.avail_out;
	}
	bool isEmpty() const { return ret_ == Z_STREAM_END; }
	void read(void *buf, size_t size)
	{
		char *p = (char *)buf;
		while (size > 0) {
			size_t readSize = readSome(p, size);
			if (readSize == 0) throw cybozu::Exception("zlib:ZlibDecompressorT:read");
			p += readSize;
			size -= readSize;
		}
	}
};

} // cybozu

<|endoftext|>


<commit_before>#include <netdb.h>
#include <unistd.h>
#include <sys/fcntl.h>

#include "client.h"
#include "duckchat.h"

// Variables
struct sockaddr_in client_addr;
struct sockaddr_in server_addr;
int client_socket;
struct addrinfo *server_info;
char *channel;

// Prints an error message and exits the program.
void Error(const char *msg) {
  std::cerr << msg << std::endl;
  exit(1);
}

// Connects to the server at a the given port.
void Connect(char *domain, const char *port) {
  std::cout << "Connecting to " << domain << std::endl;

  struct addrinfo hints;
  struct addrinfo *server_info_tmp;
  int status;

  memset(&hints, 0, sizeof(hints));
  hints.ai_family = AF_UNSPEC;
  hints.ai_socktype = SOCK_DGRAM;
  hints.ai_protocol = 0;

  if ((status = getaddrinfo(domain, port, &hints, &server_info_tmp)) != 0) {
    std::cerr << "client: unable to resolve address: " << gai_strerror(status) << std::endl;
    exit(1);
  }

  // getaddrinfo() returns a list of address structures into server_info_tmp.
  // Try each address until we successfully connect().
  // If socket() (or connect()) fails, close the socket and try the next address.

  for (server_info = server_info_tmp; server_info != NULL; server_info = server_info->ai_next) {
    if ((client_socket = socket(server_info->ai_family, server_info->ai_socktype, server_info->ai_protocol)) < 0) {
      continue;
    }
    if (connect(client_socket, server_info->ai_addr, server_info->ai_addrlen) != -1) {
      fcntl(client_socket, F_SETFL, O_NONBLOCK);
      break; // Success
    }
    close(client_socket);
  }

  if (server_info == NULL) {
    Error("client: all sockets failed to connect");
  }
}

// Sends a message to all users in on the active channel.
int RequestSay(const char *message) {
  struct request_say say;
  memset(&say, 0, sizeof(say));
  say.req_type = REQ_SAY;
  strncpy(say.req_text, message, SAY_MAX);
  strncpy(say.req_channel, channel, CHANNEL_MAX);

  if (sendto(client_socket, &say, sizeof(say), 0, server_info->ai_addr, server_info->ai_addrlen) < 0) {
    Error("client: failed to send message\n");
  }

  return 0;
}

// Sends login requests to the server.
int RequestLogin(char *username) {
  struct request_login login;
  memset(&login, 0, sizeof(login));
  login.req_type = REQ_LOGIN;
  strncpy(login.req_username, username, USERNAME_MAX);

  size_t message_size = sizeof(struct request_login);

  if (sendto(client_socket, &login, message_size, 0, server_info->ai_addr, server_info->ai_addrlen) < 0) {
    Error("client: failed to request login\n");
  }

  return 0;
}

// Sends logout requests to the server.
int RequestLogout() {
  struct request_logout logout;
  memset((char *) &logout, 0, sizeof(logout));
  logout.req_type = REQ_LOGOUT;

  size_t message_size = sizeof(struct request_logout);

  if (sendto(client_socket, &logout, message_size, 0, server_info->ai_addr, server_info->ai_addrlen) < 0) {
    Error("client: failed to request logout\n");
  }

  return 0;
}

// Sends join requests to the server.
int RequestJoin(char *channel) {
  struct request_join join;
  memset((char *) &join, 0, sizeof(join));
  join.req_type = REQ_JOIN;
  strncpy(join.req_channel, channel, CHANNEL_MAX);

  size_t message_size = sizeof(struct request_join);

  if (sendto(client_socket, &join, message_size, 0, server_info->ai_addr, server_info->ai_addrlen) < 0) {
    Error("client: failed to request join\n");
  }

  return 0;
}

// Splits strings around spaces.
std::vector<std::string> StringSplit(std::string input) {
  std::istringstream iss(input);
  std::vector<std::string> result{std::istream_iterator<std::string>{iss}, std::istream_iterator<std::string>{}};

  return result;
}

// Splits strings around spaces.
std::vector<std::string> SplitString(char *input, char delimiter) {
  std::vector<std::string> result;
  std::string word = "";

  size_t input_size = strlen(input);
  for (size_t i = 0; i < input_size; i++) {
    if (input[i] != delimiter) {
      word += input[i];
    } else {
      result.push_back(word);
      word = "";
    }
  }
  result.push_back(word);

  return result;
}

void StripChar(char *input, char c) {
  size_t size = strlen(input);
  for (size_t i = 0; i < size; i++) {
    if (input[i] == c) {
      input[i] = '\0';
    }
  }
}

// Processes the input string to decide what type of command it is.
bool ProcessInput(std::string input) {
  std::vector<std::string> inputs = StringSplit(input);
  bool result = true;

  if (inputs[0] == "/exit") {
    RequestLogout();
    result = false;
  } else if (inputs[0] == "/list") {

  } else if (inputs[0] == "/join") {

  } else if (inputs[0] == "/leave") {

  } else if (inputs[0] == "/who") {

  } else if (inputs[0] == "/switch") {

  } else {
    std::cout << "\n*Unknown command" << std::endl;
  }

  return result;
}

int main(int argc, char *argv[]) {
  char *domain;
  char *port_str;
  int port_num;
  char *username;
//  std::string input;

//  struct timeval timeout;
  fd_set read_set;
//  int file_desc = 0;
  int result;
  char receive_buffer[kBufferSize];
  memset(&receive_buffer, 0, kBufferSize);

  char stdin_buffer[kBufferSize];
  memset(&stdin_buffer, 0, kBufferSize);

  if (argc < 4) {
    Error("usage: client [server name] [port] [username]");
  }

  domain = argv[1];
  port_str = argv[2];
  port_num = atoi(argv[2]);
  username = argv[3];

  if (strlen(domain) > UNIX_PATH_MAX) {
    Error("client: server name must be less than 108 characters");
  }

  if (port_num < 0 || port_num > 65535) {
    Error("client: port number must be between 0 and 65535");
  }

  if (strlen(username) > USERNAME_MAX) {
    Error("client: username must be less than 32 characters");
  }

  Connect(domain, port_str);

  RequestLogin(username);

  channel = (char *) "Common";
  RequestJoin(channel);

  // TODO handle response from send

  while (1) {
    FD_ZERO(&read_set);
    FD_SET(client_socket, &read_set);
    FD_SET(STDIN_FILENO, &read_set);

    if ((result = select(client_socket + 1, &read_set, NULL, NULL, NULL)) < 0) {
      Error("client: problem using select");
    }

    if (result > 0) {
      if (FD_ISSET(client_socket, &read_set)) {
        // Socket has data
        int read_size = read(client_socket, receive_buffer, kBufferSize);

        if (read_size != 0) {
          // TODO capture user input, store, clean input, then print buffer, afterward replace input
          struct text message;
          memcpy(&message, receive_buffer, sizeof(struct text));
          text_t text_type = message.txt_type;

          switch (text_type) {
            case TXT_SAY:
              struct text_say say;
              memcpy(&say, receive_buffer, sizeof(struct text_say));
              std::cout << "\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b";
              std::cout << "[" << say.txt_channel << "]" << "[" << say.txt_username << "]: " << say.txt_text << std::endl;
              std::cout << ">";
              break;
            default:
              break;
          }
        }

        memset(&receive_buffer, 0, SAY_MAX);
      }

      if (FD_ISSET(STDIN_FILENO, &read_set)) {
        int read_stdin_size = read(STDIN_FILENO, stdin_buffer, kBufferSize);

        if (read_stdin_size != 0) {
          if (stdin_buffer[0] == '/') {
            ProcessInput(stdin_buffer);
          } else {
            // Send chat messages
            StripChar(stdin_buffer, '\n');
            RequestSay(stdin_buffer);
          }
        }

        memset(&stdin_buffer, 0, kBufferSize);
      } // end of if STDIN

    } // end of if result

  } // end of while

  return 0;
}<commit_msg>add std::end<commit_after>#include <netdb.h>
#include <unistd.h>
#include <sys/fcntl.h>

#include "client.h"
#include "duckchat.h"

// Variables
struct sockaddr_in client_addr;
struct sockaddr_in server_addr;
int client_socket;
struct addrinfo *server_info;
char *channel;

// Prints an error message and exits the program.
void Error(const char *msg) {
  std::cerr << msg << std::endl;
  exit(1);
}

// Connects to the server at a the given port.
void Connect(char *domain, const char *port) {
  std::cout << "Connecting to " << domain << std::endl;

  struct addrinfo hints;
  struct addrinfo *server_info_tmp;
  int status;

  memset(&hints, 0, sizeof(hints));
  hints.ai_family = AF_UNSPEC;
  hints.ai_socktype = SOCK_DGRAM;
  hints.ai_protocol = 0;

  if ((status = getaddrinfo(domain, port, &hints, &server_info_tmp)) != 0) {
    std::cerr << "client: unable to resolve address: " << gai_strerror(status) << std::endl;
    exit(1);
  }

  // getaddrinfo() returns a list of address structures into server_info_tmp.
  // Try each address until we successfully connect().
  // If socket() (or connect()) fails, close the socket and try the next address.

  for (server_info = server_info_tmp; server_info != NULL; server_info = server_info->ai_next) {
    if ((client_socket = socket(server_info->ai_family, server_info->ai_socktype, server_info->ai_protocol)) < 0) {
      continue;
    }
    if (connect(client_socket, server_info->ai_addr, server_info->ai_addrlen) != -1) {
      fcntl(client_socket, F_SETFL, O_NONBLOCK);
      break; // Success
    }
    close(client_socket);
  }

  if (server_info == NULL) {
    Error("client: all sockets failed to connect");
  }
}

// Sends a message to all users in on the active channel.
int RequestSay(const char *message) {
  struct request_say say;
  memset(&say, 0, sizeof(say));
  say.req_type = REQ_SAY;
  strncpy(say.req_text, message, SAY_MAX);
  strncpy(say.req_channel, channel, CHANNEL_MAX);

  if (sendto(client_socket, &say, sizeof(say), 0, server_info->ai_addr, server_info->ai_addrlen) < 0) {
    Error("client: failed to send message\n");
  }

  return 0;
}

// Sends login requests to the server.
int RequestLogin(char *username) {
  struct request_login login;
  memset(&login, 0, sizeof(login));
  login.req_type = REQ_LOGIN;
  strncpy(login.req_username, username, USERNAME_MAX);

  size_t message_size = sizeof(struct request_login);

  if (sendto(client_socket, &login, message_size, 0, server_info->ai_addr, server_info->ai_addrlen) < 0) {
    Error("client: failed to request login\n");
  }

  return 0;
}

// Sends logout requests to the server.
int RequestLogout() {
  struct request_logout logout;
  memset((char *) &logout, 0, sizeof(logout));
  logout.req_type = REQ_LOGOUT;

  size_t message_size = sizeof(struct request_logout);

  if (sendto(client_socket, &logout, message_size, 0, server_info->ai_addr, server_info->ai_addrlen) < 0) {
    Error("client: failed to request logout\n");
  }

  return 0;
}

// Sends join requests to the server.
int RequestJoin(char *channel) {
  struct request_join join;
  memset((char *) &join, 0, sizeof(join));
  join.req_type = REQ_JOIN;
  strncpy(join.req_channel, channel, CHANNEL_MAX);

  size_t message_size = sizeof(struct request_join);

  if (sendto(client_socket, &join, message_size, 0, server_info->ai_addr, server_info->ai_addrlen) < 0) {
    Error("client: failed to request join\n");
  }

  return 0;
}

// Splits strings around spaces.
std::vector<std::string> StringSplit(std::string input) {
  std::istringstream iss(input);
  std::vector<std::string> result{std::istream_iterator<std::string>{iss}, std::istream_iterator<std::string>{}};

  return result;
}

// Splits strings around spaces.
std::vector<std::string> SplitString(char *input, char delimiter) {
  std::vector<std::string> result;
  std::string word = "";

  size_t input_size = strlen(input);
  for (size_t i = 0; i < input_size; i++) {
    if (input[i] != delimiter) {
      word += input[i];
    } else {
      result.push_back(word);
      word = "";
    }
  }
  result.push_back(word);

  return result;
}

void StripChar(char *input, char c) {
  size_t size = strlen(input);
  for (size_t i = 0; i < size; i++) {
    if (input[i] == c) {
      input[i] = '\0';
    }
  }
}

// Processes the input string to decide what type of command it is.
bool ProcessInput(std::string input) {
  std::vector<std::string> inputs = StringSplit(input);
  bool result = true;

  if (inputs[0] == "/exit") {
    RequestLogout();
    result = false;
  } else if (inputs[0] == "/list") {

  } else if (inputs[0] == "/join") {

  } else if (inputs[0] == "/leave") {

  } else if (inputs[0] == "/who") {

  } else if (inputs[0] == "/switch") {

  } else {
    std::cout << "\n*Unknown command" << std::endl;
  }

  return result;
}

int main(int argc, char *argv[]) {
  char *domain;
  char *port_str;
  int port_num;
  char *username;
//  std::string input;

//  struct timeval timeout;
  fd_set read_set;
//  int file_desc = 0;
  int result;
  char receive_buffer[kBufferSize];
  memset(&receive_buffer, 0, kBufferSize);

  char stdin_buffer[kBufferSize];
  memset(&stdin_buffer, 0, kBufferSize);

  if (argc < 4) {
    Error("usage: client [server name] [port] [username]");
  }

  domain = argv[1];
  port_str = argv[2];
  port_num = atoi(argv[2]);
  username = argv[3];

  if (strlen(domain) > UNIX_PATH_MAX) {
    Error("client: server name must be less than 108 characters");
  }

  if (port_num < 0 || port_num > 65535) {
    Error("client: port number must be between 0 and 65535");
  }

  if (strlen(username) > USERNAME_MAX) {
    Error("client: username must be less than 32 characters");
  }

  Connect(domain, port_str);

  RequestLogin(username);

  channel = (char *) "Common";
  RequestJoin(channel);

  // TODO handle response from send

  while (1) {
    FD_ZERO(&read_set);
    FD_SET(client_socket, &read_set);
    FD_SET(STDIN_FILENO, &read_set);

    if ((result = select(client_socket + 1, &read_set, NULL, NULL, NULL)) < 0) {
      Error("client: problem using select");
    }

    if (result > 0) {
      if (FD_ISSET(client_socket, &read_set)) {
        // Socket has data
        int read_size = read(client_socket, receive_buffer, kBufferSize);

        if (read_size != 0) {
          // TODO capture user input, store, clean input, then print buffer, afterward replace input
          struct text message;
          memcpy(&message, receive_buffer, sizeof(struct text));
          text_t text_type = message.txt_type;

          switch (text_type) {
            case TXT_SAY:
              struct text_say say;
              memcpy(&say, receive_buffer, sizeof(struct text_say));
              std::cout << "\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b";
              std::cout << "[" << say.txt_channel << "]" << "[" << say.txt_username << "]: " << say.txt_text << std::endl;
              std::cout << ">" << std::endl;
              break;
            default:
              break;
          }
        }

        memset(&receive_buffer, 0, SAY_MAX);
      }

      if (FD_ISSET(STDIN_FILENO, &read_set)) {
        int read_stdin_size = read(STDIN_FILENO, stdin_buffer, kBufferSize);

        if (read_stdin_size != 0) {
          if (stdin_buffer[0] == '/') {
            ProcessInput(stdin_buffer);
          } else {
            // Send chat messages
            StripChar(stdin_buffer, '\n');
            RequestSay(stdin_buffer);
          }
        }

        memset(&stdin_buffer, 0, kBufferSize);
      } // end of if STDIN

    } // end of if result

  } // end of while

  return 0;
}<|endoftext|>


<commit_before>//===- llvm/Transforms/DecomposeMultiDimRefs.cpp - Lower array refs to 1D -===//
//
//                     The LLVM Compiler Infrastructure
//
// This file was developed by the LLVM research group and is distributed under
// the University of Illinois Open Source License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// DecomposeMultiDimRefs - Convert multi-dimensional references consisting of
// any combination of 2 or more array and structure indices into a sequence of
// instructions (using getelementpr and cast) so that each instruction has at
// most one index (except structure references, which need an extra leading
// index of [0]).
//
//===----------------------------------------------------------------------===//

#include "llvm/Transforms/Scalar.h"
#include "llvm/DerivedTypes.h"
#include "llvm/Constants.h"
#include "llvm/Constant.h"
#include "llvm/iMemory.h"
#include "llvm/iOther.h"
#include "llvm/BasicBlock.h"
#include "llvm/Pass.h"
#include "Support/Statistic.h"
using namespace llvm;

namespace {
  Statistic<> NumAdded("lowerrefs", "# of getelementptr instructions added");

  struct DecomposePass : public BasicBlockPass {
    virtual bool runOnBasicBlock(BasicBlock &BB);
  };
  RegisterOpt<DecomposePass> X("lowerrefs", "Decompose multi-dimensional "
                               "structure/array references");
}

// runOnBasicBlock - Entry point for array or structure references with multiple
// indices.
//
bool DecomposePass::runOnBasicBlock(BasicBlock &BB) {
  bool changed = false;
  for (BasicBlock::iterator II = BB.begin(); II != BB.end(); )
    if (GetElementPtrInst *gep = dyn_cast<GetElementPtrInst>(II++)) // pre-inc
      if (gep->getNumIndices() >= 2)
        changed |= DecomposeArrayRef(gep); // always modifies II
  return changed;
}

FunctionPass *llvm::createDecomposeMultiDimRefsPass() {
  return new DecomposePass();
}

// Function: DecomposeArrayRef()
//
// For any GetElementPtrInst with 2 or more array and structure indices:
//
//      opCode CompositeType* P, [uint|ubyte] idx1, ..., [uint|ubyte] idxN
//
// this function generates the foll sequence:
//
//      ptr1   = getElementPtr P,         idx1
//      ptr2   = getElementPtr ptr1,   0, idx2
//      ...
//      ptrN-1 = getElementPtr ptrN-2, 0, idxN-1
//      opCode                 ptrN-1, 0, idxN  // New-MAI
//
// Then it replaces the original instruction with this sequence,
// and replaces all uses of the original instruction with New-MAI.
// If idx1 is 0, we simply omit the first getElementPtr instruction.
//
// On return: BBI points to the instruction after the current one
//            (whether or not *BBI was replaced).
//
// Return value: true if the instruction was replaced; false otherwise.
//
bool llvm::DecomposeArrayRef(GetElementPtrInst* GEP) {
  if (GEP->getNumIndices() < 2)
    return false;

  BasicBlock *BB = GEP->getParent();
  Value *LastPtr = GEP->getPointerOperand();
  Instruction *InsertPoint = GEP->getNext(); // Insert before the next insn

  // The vector of new instructions to be created
  std::vector<Instruction*> NewInsts;

  // Process each index except the last one.
  User::const_op_iterator OI = GEP->idx_begin(), OE = GEP->idx_end();
  for (; OI+1 != OE; ++OI) {
    std::vector<Value*> Indices;

    // If this is the first index and is 0, skip it and move on!
    if (OI == GEP->idx_begin()) {
      if (*OI == ConstantInt::getNullValue((*OI)->getType()))
        continue;
    }
    else // Not the first index: include initial [0] to deref the last ptr
      Indices.push_back(Constant::getNullValue(Type::LongTy));

    Indices.push_back(*OI);

    // New Instruction: nextPtr1 = GetElementPtr LastPtr, Indices
    LastPtr = new GetElementPtrInst(LastPtr, Indices, "ptr1", InsertPoint);
    ++NumAdded;
  }

  // Now create a new instruction to replace the original one
  //
  const PointerType *PtrTy = cast<PointerType>(LastPtr->getType());

  // Get the final index vector, including an initial [0] as before.
  std::vector<Value*> Indices;
  Indices.push_back(Constant::getNullValue(Type::LongTy));
  Indices.push_back(*OI);

  Value *NewVal = new GetElementPtrInst(LastPtr, Indices, GEP->getName(),
                                        InsertPoint);

  // Replace all uses of the old instruction with the new
  GEP->replaceAllUsesWith(NewVal);

  // Now remove and delete the old instruction...
  BB->getInstList().erase(GEP);

  return true;
}

<commit_msg>Get rid of a dead variable, and fix a typo in a comment.<commit_after>//===- llvm/Transforms/DecomposeMultiDimRefs.cpp - Lower array refs to 1D -===//
//
//                     The LLVM Compiler Infrastructure
//
// This file was developed by the LLVM research group and is distributed under
// the University of Illinois Open Source License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// DecomposeMultiDimRefs - Convert multi-dimensional references consisting of
// any combination of 2 or more array and structure indices into a sequence of
// instructions (using getelementpr and cast) so that each instruction has at
// most one index (except structure references, which need an extra leading
// index of [0]).
//
//===----------------------------------------------------------------------===//

#include "llvm/Transforms/Scalar.h"
#include "llvm/DerivedTypes.h"
#include "llvm/Constants.h"
#include "llvm/Constant.h"
#include "llvm/iMemory.h"
#include "llvm/iOther.h"
#include "llvm/BasicBlock.h"
#include "llvm/Pass.h"
#include "Support/Statistic.h"
using namespace llvm;

namespace {
  Statistic<> NumAdded("lowerrefs", "# of getelementptr instructions added");

  struct DecomposePass : public BasicBlockPass {
    virtual bool runOnBasicBlock(BasicBlock &BB);
  };
  RegisterOpt<DecomposePass> X("lowerrefs", "Decompose multi-dimensional "
                               "structure/array references");
}

// runOnBasicBlock - Entry point for array or structure references with multiple
// indices.
//
bool DecomposePass::runOnBasicBlock(BasicBlock &BB) {
  bool changed = false;
  for (BasicBlock::iterator II = BB.begin(); II != BB.end(); )
    if (GetElementPtrInst *gep = dyn_cast<GetElementPtrInst>(II++)) // pre-inc
      if (gep->getNumIndices() >= 2)
        changed |= DecomposeArrayRef(gep); // always modifies II
  return changed;
}

FunctionPass *llvm::createDecomposeMultiDimRefsPass() {
  return new DecomposePass();
}

// Function: DecomposeArrayRef()
//
// For any GetElementPtrInst with 2 or more array and structure indices:
//
//      opCode CompositeType* P, [uint|ubyte] idx1, ..., [uint|ubyte] idxN
//
// this function generates the following sequence:
//
//      ptr1   = getElementPtr P,         idx1
//      ptr2   = getElementPtr ptr1,   0, idx2
//      ...
//      ptrN-1 = getElementPtr ptrN-2, 0, idxN-1
//      opCode                 ptrN-1, 0, idxN  // New-MAI
//
// Then it replaces the original instruction with this sequence,
// and replaces all uses of the original instruction with New-MAI.
// If idx1 is 0, we simply omit the first getElementPtr instruction.
//
// On return: BBI points to the instruction after the current one
//            (whether or not *BBI was replaced).
//
// Return value: true if the instruction was replaced; false otherwise.
//
bool llvm::DecomposeArrayRef(GetElementPtrInst* GEP) {
  if (GEP->getNumIndices() < 2)
    return false;

  BasicBlock *BB = GEP->getParent();
  Value *LastPtr = GEP->getPointerOperand();
  Instruction *InsertPoint = GEP->getNext(); // Insert before the next insn

  // Process each index except the last one.
  User::const_op_iterator OI = GEP->idx_begin(), OE = GEP->idx_end();
  for (; OI+1 != OE; ++OI) {
    std::vector<Value*> Indices;

    // If this is the first index and is 0, skip it and move on!
    if (OI == GEP->idx_begin()) {
      if (*OI == ConstantInt::getNullValue((*OI)->getType()))
        continue;
    }
    else // Not the first index: include initial [0] to deref the last ptr
      Indices.push_back(Constant::getNullValue(Type::LongTy));

    Indices.push_back(*OI);

    // New Instruction: nextPtr1 = GetElementPtr LastPtr, Indices
    LastPtr = new GetElementPtrInst(LastPtr, Indices, "ptr1", InsertPoint);
    ++NumAdded;
  }

  // Now create a new instruction to replace the original one
  //
  const PointerType *PtrTy = cast<PointerType>(LastPtr->getType());

  // Get the final index vector, including an initial [0] as before.
  std::vector<Value*> Indices;
  Indices.push_back(Constant::getNullValue(Type::LongTy));
  Indices.push_back(*OI);

  Value *NewVal = new GetElementPtrInst(LastPtr, Indices, GEP->getName(),
                                        InsertPoint);

  // Replace all uses of the old instruction with the new
  GEP->replaceAllUsesWith(NewVal);

  // Now remove and delete the old instruction...
  BB->getInstList().erase(GEP);

  return true;
}

<|endoftext|>