<commit_before>#include <stdexcept>
#include <sstream>
#include <fstream>
#include "db-redis.h"
#include "types.h"
#include "util.h"

#define DB_REDIS_HMGET_NUMFIELDS 30

static inline int64_t stoi64(const std::string &s)
{
	std::stringstream tmp(s);
	int64_t t;
	tmp >> t;
	return t;
}

static inline std::string i64tos(int64_t i)
{
	std::ostringstream os;
	os << i;
	return os.str();
}

std::string get_setting_default(std::string name, std::istream &is, const std::string def)
{
	try {
		return get_setting(name, is);
	} catch(std::runtime_error e) {
		return def;
	}
}

DBRedis::DBRedis(const std::string &mapdir)
{
	std::ifstream ifs((mapdir + "/world.mt").c_str());
	if(!ifs.good())
		throw std::runtime_error("Failed to read world.mt");
	std::string tmp;
	try {
		tmp = get_setting("redis_address", ifs);
		ifs.seekg(0);
		hash = get_setting("redis_hash", ifs);
		ifs.seekg(0);
	} catch(std::runtime_error e) {
		throw std::runtime_error("Set redis_address and redis_hash in world.mt to use the redis backend");
	}
	const char *addr = tmp.c_str();
	int port = stoi64(get_setting_default("redis_port", ifs, "6379"));
	ctx = redisConnect(addr, port);
	if(!ctx)
		throw std::runtime_error("Cannot allocate redis context");
	else if(ctx->err) {
		std::string err = std::string("Connection error: ") + ctx->errstr;
		redisFree(ctx);
		throw std::runtime_error(err);
	}

	loadPosCache();
}

DBRedis::~DBRedis()
{
	redisFree(ctx);
}

std::vector<BlockPos> DBRedis::getBlockPos()
{
	return posCache;
}

std::string DBRedis::replyTypeStr(int type) {
	switch(type) {
		case REDIS_REPLY_STATUS:
			return "REDIS_REPLY_STATUS";
		case REDIS_REPLY_ERROR:
			return "REDIS_REPLY_ERROR";
		case REDIS_REPLY_INTEGER:
			return "REDIS_REPLY_INTEGER";
		case REDIS_REPLY_NIL:
			return "REDIS_REPLY_NIL";
		case REDIS_REPLY_STRING:
			return "REDIS_REPLY_STRING";
		case REDIS_REPLY_ARRAY:
			return "REDIS_REPLY_ARRAY";
		default:
			return "UNKNOWN_REPLY_TYPE";
	}
}

void DBRedis::loadPosCache()
{
	redisReply *reply;
	reply = (redisReply*) redisCommand(ctx, "HKEYS %s", hash.c_str());
	if(!reply)
		throw std::runtime_error(std::string("redis command 'HKEYS %s' failed: ") + ctx->errstr);
	if(reply->type != REDIS_REPLY_ARRAY)
		throw std::runtime_error("Failed to get keys from database");
	for(size_t i = 0; i < reply->elements; i++) {
		if(reply->element[i]->type != REDIS_REPLY_STRING)
			throw std::runtime_error("Got wrong response to 'HKEYS %s' command");
		posCache.push_back(decodeBlockPos(stoi64(reply->element[i]->str)));
	}

	freeReplyObject(reply);
}

void DBRedis::HMGET(const std::vector<BlockPos> &positions, std::vector<ustring> *result)
{
	const char *argv[DB_REDIS_HMGET_NUMFIELDS + 2];
	argv[0] = "HMGET";
	argv[1] = hash.c_str();

	std::vector<BlockPos>::const_iterator position = positions.begin();
	std::size_t remaining = positions.size();
	while (remaining > 0) {
		const std::size_t batch_size =
			(remaining > DB_REDIS_HMGET_NUMFIELDS) ? DB_REDIS_HMGET_NUMFIELDS : remaining;
		redisReply *reply;
		{
			// storage to preserve std::string::c_str() validity
			std::vector<std::string> keys;
			keys.reserve(batch_size);
			for (std::size_t i = 0; i < batch_size; ++i) {
				keys.push_back(i64tos(encodeBlockPos(*position++)));
				argv[i+2] = keys.back().c_str();
			}
			reply = (redisReply*) redisCommandArgv(ctx, batch_size + 2, argv, NULL);
		}
		if(!reply) {
			throw std::runtime_error("HMGET failed");
		}
		if (reply->type != REDIS_REPLY_ARRAY) {
			freeReplyObject(reply);
			throw std::runtime_error(std::string("HMGET unexpected reply type ")
					+ replyTypeStr(reply->type));
		}
		if (reply->elements != batch_size) {
			freeReplyObject(reply);
			throw std::runtime_error("HMGET wrong number of elements");
		}
		for (std::size_t i = 0; i < batch_size; ++i) {
			redisReply *subreply = reply->element[i];
			if(!subreply) {
				throw std::runtime_error("HMGET failed");
			}
			if (subreply->type != REDIS_REPLY_STRING) {
				freeReplyObject(reply);
				throw std::runtime_error(std::string("HMGET wrong subreply type ")
						+ replyTypeStr(subreply->type));
			}
			if (subreply->len == 0) {
				freeReplyObject(reply);
				throw std::runtime_error("HMGET empty string");
			}
			result->push_back(ustring((const unsigned char *) subreply->str, subreply->len));
		}
		freeReplyObject(reply);
		remaining -= batch_size;
	}
}

void DBRedis::getBlocksOnZ(std::map<int16_t, BlockList> &blocks, int16_t zPos)
{
	std::vector<BlockPos> z_positions;
	for (std::vector<BlockPos>::const_iterator it = posCache.begin(); it != posCache.end(); ++it) {
		if (it->z != zPos) {
			continue;
		}
		z_positions.push_back(*it);
	}
	std::vector<ustring> z_blocks;
	HMGET(z_positions, &z_blocks);

	std::vector<ustring>::const_iterator z_block = z_blocks.begin();
	for (std::vector<BlockPos>::const_iterator pos = z_positions.begin();
			pos != z_positions.end();
			++pos, ++z_block) {
		blocks[pos->x].push_back(Block(*pos, *z_block));
	}
}
<commit_msg>DBRedis: Move some common code into macros<commit_after>#include <stdexcept>
#include <sstream>
#include <fstream>
#include "db-redis.h"
#include "types.h"
#include "util.h"

#define DB_REDIS_HMGET_NUMFIELDS 30

#define REPLY_TYPE_ERR(reply, desc) do { \
	throw std::runtime_error(std::string("Unexpected type for " desc ": ") \
			+ replyTypeStr((reply)->type)); \
	} while(0)

static inline int64_t stoi64(const std::string &s)
{
	std::stringstream tmp(s);
	int64_t t;
	tmp >> t;
	return t;
}

static inline std::string i64tos(int64_t i)
{
	std::ostringstream os;
	os << i;
	return os.str();
}

std::string get_setting_default(std::string name, std::istream &is, const std::string def)
{
	try {
		return get_setting(name, is);
	} catch(std::runtime_error e) {
		return def;
	}
}

DBRedis::DBRedis(const std::string &mapdir)
{
	std::ifstream ifs((mapdir + "/world.mt").c_str());
	if(!ifs.good())
		throw std::runtime_error("Failed to read world.mt");
	std::string tmp;
	try {
		tmp = get_setting("redis_address", ifs);
		ifs.seekg(0);
		hash = get_setting("redis_hash", ifs);
		ifs.seekg(0);
	} catch(std::runtime_error e) {
		throw std::runtime_error("Set redis_address and redis_hash in world.mt to use the redis backend");
	}
	const char *addr = tmp.c_str();
	int port = stoi64(get_setting_default("redis_port", ifs, "6379"));
	ctx = redisConnect(addr, port);
	if(!ctx)
		throw std::runtime_error("Cannot allocate redis context");
	else if(ctx->err) {
		std::string err = std::string("Connection error: ") + ctx->errstr;
		redisFree(ctx);
		throw std::runtime_error(err);
	}

	loadPosCache();
}

DBRedis::~DBRedis()
{
	redisFree(ctx);
}

std::vector<BlockPos> DBRedis::getBlockPos()
{
	return posCache;
}

std::string DBRedis::replyTypeStr(int type) {
	switch(type) {
		case REDIS_REPLY_STATUS:
			return "REDIS_REPLY_STATUS";
		case REDIS_REPLY_ERROR:
			return "REDIS_REPLY_ERROR";
		case REDIS_REPLY_INTEGER:
			return "REDIS_REPLY_INTEGER";
		case REDIS_REPLY_NIL:
			return "REDIS_REPLY_NIL";
		case REDIS_REPLY_STRING:
			return "REDIS_REPLY_STRING";
		case REDIS_REPLY_ARRAY:
			return "REDIS_REPLY_ARRAY";
		default:
			return "unknown";
	}
}

void DBRedis::loadPosCache()
{
	redisReply *reply;
	reply = (redisReply*) redisCommand(ctx, "HKEYS %s", hash.c_str());
	if(!reply)
		throw std::runtime_error("Redis command HKEYS failed");
	if(reply->type != REDIS_REPLY_ARRAY)
		REPLY_TYPE_ERR(reply, "HKEYS reply");
	for(size_t i = 0; i < reply->elements; i++) {
		if(reply->element[i]->type != REDIS_REPLY_STRING)
			REPLY_TYPE_ERR(reply->element[i], "HKEYS subreply");
		posCache.push_back(decodeBlockPos(stoi64(reply->element[i]->str)));
	}

	freeReplyObject(reply);
}

void DBRedis::HMGET(const std::vector<BlockPos> &positions, std::vector<ustring> *result)
{
	const char *argv[DB_REDIS_HMGET_NUMFIELDS + 2];
	argv[0] = "HMGET";
	argv[1] = hash.c_str();

	std::vector<BlockPos>::const_iterator position = positions.begin();
	std::size_t remaining = positions.size();
	while (remaining > 0) {
		const std::size_t batch_size =
			(remaining > DB_REDIS_HMGET_NUMFIELDS) ? DB_REDIS_HMGET_NUMFIELDS : remaining;

		redisReply *reply;
		{
			// storage to preserve validity of .c_str()
			std::string keys[batch_size];
			for (std::size_t i = 0; i < batch_size; ++i) {
				keys[i] = i64tos(encodeBlockPos(*position++));
				argv[i+2] = keys[i].c_str();
			}
			reply = (redisReply*) redisCommandArgv(ctx, batch_size + 2, argv, NULL);
		}

		if(!reply)
			throw std::runtime_error("Redis command HMGET failed");
		if (reply->type != REDIS_REPLY_ARRAY) {
			freeReplyObject(reply);
			REPLY_TYPE_ERR(reply, "HKEYS subreply");
		}
		if (reply->elements != batch_size) {
			freeReplyObject(reply);
			throw std::runtime_error("HMGET wrong number of elements");
		}
		for (std::size_t i = 0; i < batch_size; ++i) {
			redisReply *subreply = reply->element[i];
			if(!subreply)
				throw std::runtime_error("Redis command HMGET failed");
			if (subreply->type != REDIS_REPLY_STRING) {
				freeReplyObject(reply);
				REPLY_TYPE_ERR(reply, "HKEYS subreply");
			}
			if (subreply->len == 0) {
				freeReplyObject(reply);
				throw std::runtime_error("HMGET empty string");
			}
			result->push_back(ustring((const unsigned char *) subreply->str, subreply->len));
		}
		freeReplyObject(reply);
		remaining -= batch_size;
	}
}

void DBRedis::getBlocksOnZ(std::map<int16_t, BlockList> &blocks, int16_t zPos)
{
	std::vector<BlockPos> z_positions;
	for (std::vector<BlockPos>::const_iterator it = posCache.begin(); it != posCache.end(); ++it) {
		if (it->z != zPos) {
			continue;
		}
		z_positions.push_back(*it);
	}
	std::vector<ustring> z_blocks;
	HMGET(z_positions, &z_blocks);

	std::vector<ustring>::const_iterator z_block = z_blocks.begin();
	for (std::vector<BlockPos>::const_iterator pos = z_positions.begin();
			pos != z_positions.end();
			++pos, ++z_block) {
		blocks[pos->x].push_back(Block(*pos, *z_block));
	}
}
<|endoftext|>


<commit_before>//  Copyright 2020 Tangent Animation
//
//  Licensed under the Apache License, Version 2.0 (the "License");
//  you may not use this file except in compliance with the License.
//  You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
//  Unless required by applicable law or agreed to in writing, software
//  distributed under the License is distributed on an "AS IS" BASIS,
//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied,
//  including without limitation, as related to merchantability and fitness
//  for a particular purpose.
//
//  In no event shall any copyright holder be liable for any damages of any kind
//  arising from the use of this software, whether in contract, tort or otherwise.
//  See the License for the specific language governing permissions and
//  limitations under the License.

#include "renderPass.h"

#include "camera.h"
#include "renderBuffer.h"
#include "renderParam.h"
#include "utils.h"

#include <render/camera.h>
#include <render/scene.h>
#include <render/session.h>
#include <util/util_types.h>

#include <pxr/base/tf/staticTokens.h>
#include <pxr/imaging/hd/camera.h>
#include <pxr/imaging/hd/renderPassState.h>

PXR_NAMESPACE_OPEN_SCOPE

// clang-format off
#pragma GCC diagnostic push
#pragma GCC diagnostic ignore "-Wunused-variable"
TF_DEFINE_PRIVATE_TOKENS(_tokens,
    (color)
    (depth)
);
#pragma GCC diagnostic pop
// clang-format on

HdCyclesRenderPass::HdCyclesRenderPass(HdCyclesRenderDelegate* delegate,
                                       HdRenderIndex* index,
                                       HdRprimCollection const& collection)
    : HdRenderPass(index, collection)
    , m_delegate(delegate)
{
}

HdCyclesRenderPass::~HdCyclesRenderPass() {}

void
HdCyclesRenderPass::_Execute(HdRenderPassStateSharedPtr const& renderPassState,
                             TfTokenVector const& renderTags)
{
    auto* renderParam = reinterpret_cast<HdCyclesRenderParam*>(
        m_delegate->GetRenderParam());

    HdRenderPassAovBindingVector aovBindings = renderPassState->GetAovBindings();

    if (renderParam->GetAovBindings() != aovBindings)
        renderParam->SetAovBindings(aovBindings);

    const auto vp = renderPassState->GetViewport();

    GfMatrix4d projMtx = renderPassState->GetProjectionMatrix();
    GfMatrix4d viewMtx = renderPassState->GetWorldToViewMatrix();

    m_isConverged = renderParam->IsConverged();

    // XXX: Need to cast away constness to process updated camera params since
    // the Hydra camera doesn't update the Cycles camera directly.
    HdCyclesCamera* hdCam = const_cast<HdCyclesCamera*>(
        dynamic_cast<HdCyclesCamera const*>(renderPassState->GetCamera()));

    ccl::Camera* active_camera = renderParam->GetCyclesSession()->scene->camera;

    bool shouldUpdate = false;

    if (projMtx != m_projMtx || viewMtx != m_viewMtx) {
        m_projMtx = projMtx;
        m_viewMtx = viewMtx;

        const float fov_rad = atan(1.0f / m_projMtx[1][1]) * 2.0f;
        hdCam->SetFOV(fov_rad);

        shouldUpdate = true;
    }

    if(!shouldUpdate)
        shouldUpdate = hdCam->IsDirty();

    if (shouldUpdate) {
        hdCam->ApplyCameraSettings(active_camera);

        // Needed for now, as houdini looks through a generated camera
        // and doesn't copy the projection type (as of 18.0.532)
        bool is_ortho = round(m_projMtx[3][3]) == 1.0;

        if (is_ortho) {
            active_camera->type = ccl::CameraType::CAMERA_ORTHOGRAPHIC;
        } else
            active_camera->type = ccl::CameraType::CAMERA_PERSPECTIVE;

        active_camera->tag_update();

        // DirectReset here instead of Interrupt for faster IPR camera orbits
        renderParam->DirectReset();
    }

    const auto width     = static_cast<int>(vp[2]);
    const auto height    = static_cast<int>(vp[3]);
    const auto numPixels = static_cast<size_t>(width * height);

    if (width != m_width || height != m_height) {
        const auto oldNumPixels = static_cast<size_t>(m_width * m_height);
        m_width                 = width;
        m_height                = height;

        // TODO: Due to the startup flow of Cycles, this gets called after a tiled render
        // has already started. Sometimes causing the original tiled render to complete
        // before actually rendering at the appropriate size. This seems to be a Cycles
        // issue, however the startup flow of HdCycles has LOTS of room for improvement...
        renderParam->SetViewport(m_width, m_height);

        // TODO: This is very hacky... But stops the tiled render double render issue...
        if (renderParam->IsTiledRender()) {
            renderParam->StartRender();
        }

        renderParam->Interrupt();
    }

    // Tiled renders early out because we do the blitting on render tile callback
    if (renderParam->IsTiledRender())
        return;

    if (!renderParam->GetCyclesSession())
        return;

    if (!renderParam->GetCyclesScene())
        return;

    ccl::DisplayBuffer* display = renderParam->GetCyclesSession()->display;

    if (!display)
        return;

    HdFormat colorFormat = display->half_float ? HdFormatFloat16Vec4
                                               : HdFormatUNorm8Vec4;

    unsigned char* hpixels
        = (display->half_float)
              ? (unsigned char*)display->rgba_half.host_pointer
              : (unsigned char*)display->rgba_byte.host_pointer;

    if (!hpixels)
        return;

    int w = display->draw_width;
    int h = display->draw_height;

    if (w == 0 || h == 0)
        return;

    // Blit
    if (!aovBindings.empty()) {
        // Blit from the framebuffer to currently selected aovs...
        for (auto& aov : aovBindings) {
            if (!TF_VERIFY(aov.renderBuffer != nullptr)) {
                continue;
            }

            auto* rb = static_cast<HdCyclesRenderBuffer*>(aov.renderBuffer);
            rb->SetConverged(m_isConverged);

            // Needed as a stopgap, because Houdini dellocates renderBuffers
            // when changing render settings. This causes the current blit to
            // fail (Probably can be fixed with proper render thread management)
            if (!rb->WasUpdated()) {
                if (aov.aovName == HdAovTokens->color) {
                    rb->Blit(colorFormat, w, h, 0, w,
                             reinterpret_cast<uint8_t*>(hpixels));
                }
            } else {
                rb->SetWasUpdated(false);
            }
        }
    }
}

PXR_NAMESPACE_CLOSE_SCOPE
<commit_msg>Fixed typo.<commit_after>//  Copyright 2020 Tangent Animation
//
//  Licensed under the Apache License, Version 2.0 (the "License");
//  you may not use this file except in compliance with the License.
//  You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
//  Unless required by applicable law or agreed to in writing, software
//  distributed under the License is distributed on an "AS IS" BASIS,
//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied,
//  including without limitation, as related to merchantability and fitness
//  for a particular purpose.
//
//  In no event shall any copyright holder be liable for any damages of any kind
//  arising from the use of this software, whether in contract, tort or otherwise.
//  See the License for the specific language governing permissions and
//  limitations under the License.

#include "renderPass.h"

#include "camera.h"
#include "renderBuffer.h"
#include "renderParam.h"
#include "utils.h"

#include <render/camera.h>
#include <render/scene.h>
#include <render/session.h>
#include <util/util_types.h>

#include <pxr/base/tf/staticTokens.h>
#include <pxr/imaging/hd/camera.h>
#include <pxr/imaging/hd/renderPassState.h>

PXR_NAMESPACE_OPEN_SCOPE

// clang-format off
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-variable"
TF_DEFINE_PRIVATE_TOKENS(_tokens,
    (color)
    (depth)
);
#pragma GCC diagnostic pop
// clang-format on

HdCyclesRenderPass::HdCyclesRenderPass(HdCyclesRenderDelegate* delegate,
                                       HdRenderIndex* index,
                                       HdRprimCollection const& collection)
    : HdRenderPass(index, collection)
    , m_delegate(delegate)
{
}

HdCyclesRenderPass::~HdCyclesRenderPass() {}

void
HdCyclesRenderPass::_Execute(HdRenderPassStateSharedPtr const& renderPassState,
                             TfTokenVector const& renderTags)
{
    auto* renderParam = reinterpret_cast<HdCyclesRenderParam*>(
        m_delegate->GetRenderParam());

    HdRenderPassAovBindingVector aovBindings = renderPassState->GetAovBindings();

    if (renderParam->GetAovBindings() != aovBindings)
        renderParam->SetAovBindings(aovBindings);

    const auto vp = renderPassState->GetViewport();

    GfMatrix4d projMtx = renderPassState->GetProjectionMatrix();
    GfMatrix4d viewMtx = renderPassState->GetWorldToViewMatrix();

    m_isConverged = renderParam->IsConverged();

    // XXX: Need to cast away constness to process updated camera params since
    // the Hydra camera doesn't update the Cycles camera directly.
    HdCyclesCamera* hdCam = const_cast<HdCyclesCamera*>(
        dynamic_cast<HdCyclesCamera const*>(renderPassState->GetCamera()));

    ccl::Camera* active_camera = renderParam->GetCyclesSession()->scene->camera;

    bool shouldUpdate = false;

    if (projMtx != m_projMtx || viewMtx != m_viewMtx) {
        m_projMtx = projMtx;
        m_viewMtx = viewMtx;

        const float fov_rad = atan(1.0f / m_projMtx[1][1]) * 2.0f;
        hdCam->SetFOV(fov_rad);

        shouldUpdate = true;
    }

    if(!shouldUpdate)
        shouldUpdate = hdCam->IsDirty();

    if (shouldUpdate) {
        hdCam->ApplyCameraSettings(active_camera);

        // Needed for now, as houdini looks through a generated camera
        // and doesn't copy the projection type (as of 18.0.532)
        bool is_ortho = round(m_projMtx[3][3]) == 1.0;

        if (is_ortho) {
            active_camera->type = ccl::CameraType::CAMERA_ORTHOGRAPHIC;
        } else
            active_camera->type = ccl::CameraType::CAMERA_PERSPECTIVE;

        active_camera->tag_update();

        // DirectReset here instead of Interrupt for faster IPR camera orbits
        renderParam->DirectReset();
    }

    const auto width     = static_cast<int>(vp[2]);
    const auto height    = static_cast<int>(vp[3]);
    const auto numPixels = static_cast<size_t>(width * height);

    if (width != m_width || height != m_height) {
        const auto oldNumPixels = static_cast<size_t>(m_width * m_height);
        m_width                 = width;
        m_height                = height;

        // TODO: Due to the startup flow of Cycles, this gets called after a tiled render
        // has already started. Sometimes causing the original tiled render to complete
        // before actually rendering at the appropriate size. This seems to be a Cycles
        // issue, however the startup flow of HdCycles has LOTS of room for improvement...
        renderParam->SetViewport(m_width, m_height);

        // TODO: This is very hacky... But stops the tiled render double render issue...
        if (renderParam->IsTiledRender()) {
            renderParam->StartRender();
        }

        renderParam->Interrupt();
    }

    // Tiled renders early out because we do the blitting on render tile callback
    if (renderParam->IsTiledRender())
        return;

    if (!renderParam->GetCyclesSession())
        return;

    if (!renderParam->GetCyclesScene())
        return;

    ccl::DisplayBuffer* display = renderParam->GetCyclesSession()->display;

    if (!display)
        return;

    HdFormat colorFormat = display->half_float ? HdFormatFloat16Vec4
                                               : HdFormatUNorm8Vec4;

    unsigned char* hpixels
        = (display->half_float)
              ? (unsigned char*)display->rgba_half.host_pointer
              : (unsigned char*)display->rgba_byte.host_pointer;

    if (!hpixels)
        return;

    int w = display->draw_width;
    int h = display->draw_height;

    if (w == 0 || h == 0)
        return;

    // Blit
    if (!aovBindings.empty()) {
        // Blit from the framebuffer to currently selected aovs...
        for (auto& aov : aovBindings) {
            if (!TF_VERIFY(aov.renderBuffer != nullptr)) {
                continue;
            }

            auto* rb = static_cast<HdCyclesRenderBuffer*>(aov.renderBuffer);
            rb->SetConverged(m_isConverged);

            // Needed as a stopgap, because Houdini dellocates renderBuffers
            // when changing render settings. This causes the current blit to
            // fail (Probably can be fixed with proper render thread management)
            if (!rb->WasUpdated()) {
                if (aov.aovName == HdAovTokens->color) {
                    rb->Blit(colorFormat, w, h, 0, w,
                             reinterpret_cast<uint8_t*>(hpixels));
                }
            } else {
                rb->SetWasUpdated(false);
            }
        }
    }
}

PXR_NAMESPACE_CLOSE_SCOPE
<|endoftext|>


<commit_before>#line 2 "togo/core/string/string.cpp"
/**
@copyright MIT license; see @ref index or the accompanying LICENSE file.
*/

#include <togo/core/config.hpp>
#include <togo/core/error/assert.hpp>
#include <togo/core/string/string.hpp>

#include <cstring>

namespace togo {

/// Compare two strings for equality.
///
/// This will short-circuit if lhs and rhs are not the same size.
bool string::compare_equal(StringRef const& lhs, StringRef const& rhs) {
	// TODO: Use memcmp()/block-wise compare?
	if (lhs.size != rhs.size) {
		return false;
	}
	unsigned size = lhs.size;
	char const* lp = lhs.data;
	char const* rp = rhs.data;
	while (size--) {
		if (*lp++ != *rp++) {
			return false;
		}
	}
	return true;
}

/// Copy string.
///
/// dst will be NUL-terminated.
/// The capacity of dst must be at least src.size + 1.
void string::copy(char* const dst, unsigned const capacity, StringRef const& src) {
	TOGO_ASSERT(capacity > src.size, "dst not large enough to store src + NUL");
	if (!src.empty()) {
		std::memcpy(dst, src.data, src.size);
	}
	dst[src.size] = '\0';
}

/// Trim trailing slashes from string.
///
/// The first trailing slash is replaced by a NUL.
/// Returns new size of string (not including NUL terminator).
unsigned string::trim_trailing_slashes(char* const string, unsigned const size) {
	TOGO_ASSERTE(string != nullptr);
	char* it = (string + size) - 1;
	while (it >= string && (*it == '/' || *it == '\\')) {
		--it;
	}
	++it;
	unsigned const new_size = it - string;
	if (new_size != size) {
		*it = '\0';
	}
	return new_size;
}

/// Ensure string has a trailing slash by appending if necessary.
///
/// str will be NUL terminated if it is modified.
/// If str is modified, its capacity must be at least size + 2.
/// Returns new size of str (not including NUL terminator).
unsigned string::ensure_trailing_slash(
	char* str,
	unsigned capacity,
	unsigned size
) {
	if (size == 0 || (str[size - 1] != '/' && str[size - 1] != '\\')) {
		TOGO_ASSERT(capacity > size + 2, "str not large enough to store slash and NUL");
		str[size++] = '/';
		str[size] = '\0';
	}
	return size;
}

} // namespace togo
<commit_msg>lib/core/string/string: tidy.<commit_after>#line 2 "togo/core/string/string.cpp"
/**
@copyright MIT license; see @ref index or the accompanying LICENSE file.
*/

#include <togo/core/config.hpp>
#include <togo/core/error/assert.hpp>
#include <togo/core/string/string.hpp>

#include <cstring>

namespace togo {

/// Compare two strings for equality.
///
/// This will short-circuit if lhs and rhs are not the same size.
bool string::compare_equal(StringRef const& lhs, StringRef const& rhs) {
	// TODO: Use memcmp()/block-wise compare?
	if (lhs.size != rhs.size) {
		return false;
	}
	unsigned size = lhs.size;
	char const* lp = lhs.data;
	char const* rp = rhs.data;
	while (size--) {
		if (*lp++ != *rp++) {
			return false;
		}
	}
	return true;
}

/// Copy string.
///
/// dst will be NUL-terminated.
/// The capacity of dst must be at least src.size + 1.
void string::copy(char* const dst, unsigned const capacity, StringRef const& src) {
	TOGO_ASSERT(capacity > src.size, "dst not large enough to store src + NUL");
	if (src.any()) {
		std::memcpy(dst, src.data, src.size);
	}
	dst[src.size] = '\0';
}

/// Trim trailing slashes from string.
///
/// The first trailing slash is replaced by a NUL.
/// Returns new size of string (not including NUL terminator).
unsigned string::trim_trailing_slashes(char* const string, unsigned const size) {
	TOGO_ASSERTE(string != nullptr);
	char* it = (string + size) - 1;
	while (it >= string && (*it == '/' || *it == '\\')) {
		--it;
	}
	++it;
	unsigned const new_size = it - string;
	if (new_size != size) {
		*it = '\0';
	}
	return new_size;
}

/// Ensure string has a trailing slash by appending if necessary.
///
/// str will be NUL terminated if it is modified.
/// If str is modified, its capacity must be at least size + 2.
/// Returns new size of str (not including NUL terminator).
unsigned string::ensure_trailing_slash(
	char* str,
	unsigned capacity,
	unsigned size
) {
	if (size == 0 || (str[size - 1] != '/' && str[size - 1] != '\\')) {
		TOGO_ASSERT(capacity > size + 2, "str not large enough to store slash and NUL");
		str[size++] = '/';
		str[size] = '\0';
	}
	return size;
}

} // namespace togo
<|endoftext|>


<commit_before>/*
* Copyright (C) 2008-2011 J-P Nurmi <jpnurmi@gmail.com>
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
* This library is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
* License for more details.
*/

#include <QtDeclarative>
#include <IrcSession>
#include <IrcCommand>
#include <IrcMessage>
#include <IrcSender>
#include <Irc>

class DeclarativeIrcSender : public QObject
{
    Q_OBJECT

public:
    explicit DeclarativeIrcSender(QObject* parent = 0) : QObject(parent) { }

    Q_INVOKABLE static QString name(const IrcSender& sender)
    {
        return sender.name();
    }

    Q_INVOKABLE static QString user(const IrcSender& sender)
    {
        return sender.user();
    }

    Q_INVOKABLE static QString host(const IrcSender& sender)
    {
        return sender.host();
    }
};

QML_DECLARE_TYPE(Irc)
QML_DECLARE_TYPE(IrcCommand)
QML_DECLARE_TYPE(IrcMessage)
QML_DECLARE_TYPE(IrcSession)
QML_DECLARE_TYPE(DeclarativeIrcSender)

class CommuniPlugin : public QDeclarativeExtensionPlugin
{
    Q_OBJECT

public:
    virtual void initializeEngine(QDeclarativeEngine* engine, const char* uri)
    {
        QDeclarativeExtensionPlugin::initializeEngine(engine, uri);

        QDeclarativeContext* context = engine->rootContext();
        context->setContextProperty("IrcSender", new DeclarativeIrcSender(context));
    }

    void registerTypes(const char *uri)
    {
        qmlRegisterType<IrcSession>(uri, 1, 0, "IrcSession");
        qmlRegisterType<IrcCommand>(uri, 1, 0, "IrcCommand");

        qmlRegisterUncreatableType<Irc>(uri, 1, 0, "Irc", "");
        qmlRegisterUncreatableType<IrcMessage>(uri, 1, 0, "IrcMessage", "");
    }
};

#include "plugin.moc"

Q_EXPORT_PLUGIN2(communiplugin, CommuniPlugin);
<commit_msg>declarative plugin: made IrcMessage a creatable type<commit_after>/*
* Copyright (C) 2008-2011 J-P Nurmi <jpnurmi@gmail.com>
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
* This library is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
* License for more details.
*/

#include <QtDeclarative>
#include <IrcSession>
#include <IrcCommand>
#include <IrcMessage>
#include <IrcSender>
#include <Irc>

class DeclarativeIrcSender : public QObject
{
    Q_OBJECT

public:
    explicit DeclarativeIrcSender(QObject* parent = 0) : QObject(parent) { }

    Q_INVOKABLE static QString name(const IrcSender& sender)
    {
        return sender.name();
    }

    Q_INVOKABLE static QString user(const IrcSender& sender)
    {
        return sender.user();
    }

    Q_INVOKABLE static QString host(const IrcSender& sender)
    {
        return sender.host();
    }
};

QML_DECLARE_TYPE(Irc)
QML_DECLARE_TYPE(IrcCommand)
QML_DECLARE_TYPE(IrcMessage)
QML_DECLARE_TYPE(IrcSession)
QML_DECLARE_TYPE(DeclarativeIrcSender)

class CommuniPlugin : public QDeclarativeExtensionPlugin
{
    Q_OBJECT

public:
    virtual void initializeEngine(QDeclarativeEngine* engine, const char* uri)
    {
        QDeclarativeExtensionPlugin::initializeEngine(engine, uri);

        QDeclarativeContext* context = engine->rootContext();
        context->setContextProperty("IrcSender", new DeclarativeIrcSender(context));
    }

    void registerTypes(const char *uri)
    {
        qmlRegisterType<IrcSession>(uri, 1, 0, "IrcSession");
        qmlRegisterType<IrcCommand>(uri, 1, 0, "IrcCommand");
        qmlRegisterType<IrcMessage>(uri, 1, 0, "IrcMessage");

        qmlRegisterUncreatableType<Irc>(uri, 1, 0, "Irc", "");
    }
};

#include "plugin.moc"

Q_EXPORT_PLUGIN2(communiplugin, CommuniPlugin);
<|endoftext|>


<commit_before>#include "3rd-party/catch.hpp"

#include <memory>

#include "cache.h"
#include "configcontainer.h"
#include "feedhandler.h"
#include "rss.h"
#include "test-helpers.h"

using namespace newsboat;

namespace {

std::vector<std::shared_ptr<rss_feed>> get_five_empty_feeds(cache* rsscache)
{
	std::vector<std::shared_ptr<rss_feed>> feeds;
	for (int i = 0; i < 5; ++i) {
		const auto feed = std::make_shared<rss_feed>(rsscache);
		feeds.push_back(feed);
	}
	return feeds;
}

} // anonymous namespace

TEST_CASE("get_feed() returns feed by its position number", "[feedhandler]")
{
	FeedHandler feedhandler;
	std::unique_ptr<configcontainer> cfg(new configcontainer());
	std::unique_ptr<cache> rsscache(new cache(":memory:", cfg.get()));
	const auto feeds = get_five_empty_feeds(rsscache.get());
	int i = 0;
	for (const auto& feed : feeds) {
		feed->set_title(std::to_string(i));
		feed->set_rssurl("url/" + std::to_string(i));
		i++;
	}
	feedhandler.set_feeds(feeds);

	auto feed = feedhandler.get_feed(0);
	REQUIRE(feed->title_raw() == "0");

	feed = feedhandler.get_feed(4);
	REQUIRE(feed->title_raw() == "4");
}

TEST_CASE("get_feed_by_url() returns feed by its URL", "[feedhandler]")
{
	FeedHandler feedhandler;
	std::unique_ptr<configcontainer> cfg(new configcontainer());
	std::unique_ptr<cache> rsscache(new cache(":memory:", cfg.get()));
	const auto feeds = get_five_empty_feeds(rsscache.get());
	int i = 0;
	for (const auto& feed : feeds) {
		feed->set_title(std::to_string(i));
		feed->set_rssurl("url/" + std::to_string(i));
		i++;
	}
	feedhandler.set_feeds(feeds);

	auto feed = feedhandler.get_feed_by_url("url/1");
	REQUIRE(feed->title_raw() == "1");

	feed = feedhandler.get_feed_by_url("url/4");
	REQUIRE(feed->title_raw() == "4");
}

TEST_CASE("Throws on get_feed() with pos out of range", "[feedhandler]")
{
	FeedHandler feedhandler;
	std::unique_ptr<configcontainer> cfg(new configcontainer());
	std::unique_ptr<cache> rsscache(new cache(":memory:", cfg.get()));
	feedhandler.set_feeds(get_five_empty_feeds(rsscache.get()));

	REQUIRE_NOTHROW(feedhandler.get_feed(4));
	CHECK_THROWS_AS(feedhandler.get_feed(5), std::out_of_range);
	CHECK_THROWS_AS(feedhandler.get_feed(-1), std::out_of_range);
}

TEST_CASE("Returns correct number using get_feed_count_by_tag()", "[feedhandler]")
{
	FeedHandler feedhandler;
	std::unique_ptr<configcontainer> cfg(new configcontainer());
	std::unique_ptr<cache> rsscache(new cache(":memory:", cfg.get()));
	feedhandler.set_feeds(get_five_empty_feeds(rsscache.get()));
	feedhandler.get_feed(0)->set_tags({"Chicken", "Horse"});
	feedhandler.get_feed(1)->set_tags({"Horse", "Duck"});
	feedhandler.get_feed(2)->set_tags({"Duck", "Frog"});
	feedhandler.get_feed(3)->set_tags({"Duck", "Hawk"});

	REQUIRE(feedhandler.get_feed_count_per_tag("Ice Cream") == 0);
	REQUIRE(feedhandler.get_feed_count_per_tag("Chicken") == 1);
	REQUIRE(feedhandler.get_feed_count_per_tag("Horse") == 2);
	REQUIRE(feedhandler.get_feed_count_per_tag("Duck") == 3);
}

TEST_CASE("Correctly returns pos of next unread item", "[feedhandler]")
{
	FeedHandler feedhandler;
	std::unique_ptr<configcontainer> cfg(new configcontainer());
	std::unique_ptr<cache> rsscache(new cache(":memory:", cfg.get()));
	const auto feeds = get_five_empty_feeds(rsscache.get());
	int i = 0;
	for (const auto& feed : feeds) {
		const auto item = std::make_shared<rss_item>(rsscache.get());
		if ((i % 2) == 0)
			item->set_unread_nowrite(true);
		else
			item->set_unread_nowrite(false);
		feed->add_item(item);
		i++;
	}
	feedhandler.set_feeds(feeds);

	REQUIRE(feedhandler.get_pos_of_next_unread(0) == 2);
	REQUIRE(feedhandler.get_pos_of_next_unread(2) == 4);
}

TEST_CASE("Correctly sorts feeds", "[feedhandler]")
{
	SECTION("by none asc")
	{
	}

	SECTION("by none desc")
	{
	}

	SECTION("by firsttag asc")
	{
	}

	SECTION("by firsttag desc")
	{
	}

	SECTION("by title asc")
	{
	}

	SECTION("by title desc")
	{
	}

	SECTION("by articlecount asc")
	{
	}

	SECTION("by articlecount desc")
	{
	}

	SECTION("by unreadarticlecount asc")
	{
	}

	SECTION("by unreadarticlecount desc")
	{
	}

	SECTION("by lastupdated asc")
	{
	}

	SECTION("by lastupdated desc")
	{
	}
}

TEST_CASE("mark_all_feed_items_read() marks all of feed's items as read", "[feedhandler]")
{
	FeedHandler feedhandler;
	std::unique_ptr<configcontainer> cfg(new configcontainer());
	std::unique_ptr<cache> rsscache(new cache(":memory:", cfg.get()));
	const auto feeds = get_five_empty_feeds(rsscache.get());
	const auto feed = feeds.at(0);
	for (int j = 0; j < 5; ++j) {
		const auto item = std::make_shared<rss_item>(rsscache.get());
		item->set_unread_nowrite(true);
		feed->add_item(item);
	}
	feedhandler.set_feeds(feeds);

	feedhandler.mark_all_feed_items_read(0);

	for (const auto& item : feed->items()) {
		REQUIRE(item->unread() == false);
	}
}

TEST_CASE("reset_feeds_status() resets status of all feeds", "[feedhandler]")
{
	FeedHandler feedhandler;
	std::unique_ptr<configcontainer> cfg(new configcontainer());
	std::unique_ptr<cache> rsscache(new cache(":memory:", cfg.get()));
	const auto feeds = get_five_empty_feeds(rsscache.get());
	for (const auto& feed : feeds) {
		feed->set_status(dl_status::DL_ERROR);
	}
	feedhandler.set_feeds(feeds);

	feedhandler.reset_feeds_status();

	for (const auto& feed : feeds) {
		REQUIRE(feed->get_status() == "_");
	}
}

TEST_CASE("clear_feeds_items() clears all of feed's items", "[feedhandler]")
{
	FeedHandler feedhandler;
	std::unique_ptr<configcontainer> cfg(new configcontainer());
	std::unique_ptr<cache> rsscache(new cache(":memory:", cfg.get()));
	feedhandler.set_feeds({});
	const auto feed = std::make_shared<rss_feed>(rsscache.get());
	for (int j = 0; j < 5; ++j) {
		feed->add_item(std::make_shared<rss_item>(rsscache.get()));
	}
	feedhandler.add_feed(feed);

	REQUIRE(feed->items().size() == 5);
	feedhandler.clear_feeds_items();
	REQUIRE(feed->items().size() == 0);
}
<commit_msg>Apply clang-format<commit_after>#include "3rd-party/catch.hpp"

#include <memory>

#include "cache.h"
#include "configcontainer.h"
#include "feedhandler.h"
#include "rss.h"
#include "test-helpers.h"

using namespace newsboat;

namespace {

std::vector<std::shared_ptr<rss_feed>> get_five_empty_feeds(cache* rsscache)
{
	std::vector<std::shared_ptr<rss_feed>> feeds;
	for (int i = 0; i < 5; ++i) {
		const auto feed = std::make_shared<rss_feed>(rsscache);
		feeds.push_back(feed);
	}
	return feeds;
}

} // anonymous namespace

TEST_CASE("get_feed() returns feed by its position number", "[feedhandler]")
{
	FeedHandler feedhandler;
	std::unique_ptr<configcontainer> cfg(new configcontainer());
	std::unique_ptr<cache> rsscache(new cache(":memory:", cfg.get()));
	const auto feeds = get_five_empty_feeds(rsscache.get());
	int i = 0;
	for (const auto& feed : feeds) {
		feed->set_title(std::to_string(i));
		feed->set_rssurl("url/" + std::to_string(i));
		i++;
	}
	feedhandler.set_feeds(feeds);

	auto feed = feedhandler.get_feed(0);
	REQUIRE(feed->title_raw() == "0");

	feed = feedhandler.get_feed(4);
	REQUIRE(feed->title_raw() == "4");
}

TEST_CASE("get_feed_by_url() returns feed by its URL", "[feedhandler]")
{
	FeedHandler feedhandler;
	std::unique_ptr<configcontainer> cfg(new configcontainer());
	std::unique_ptr<cache> rsscache(new cache(":memory:", cfg.get()));
	const auto feeds = get_five_empty_feeds(rsscache.get());
	int i = 0;
	for (const auto& feed : feeds) {
		feed->set_title(std::to_string(i));
		feed->set_rssurl("url/" + std::to_string(i));
		i++;
	}
	feedhandler.set_feeds(feeds);

	auto feed = feedhandler.get_feed_by_url("url/1");
	REQUIRE(feed->title_raw() == "1");

	feed = feedhandler.get_feed_by_url("url/4");
	REQUIRE(feed->title_raw() == "4");
}

TEST_CASE("Throws on get_feed() with pos out of range", "[feedhandler]")
{
	FeedHandler feedhandler;
	std::unique_ptr<configcontainer> cfg(new configcontainer());
	std::unique_ptr<cache> rsscache(new cache(":memory:", cfg.get()));
	feedhandler.set_feeds(get_five_empty_feeds(rsscache.get()));

	REQUIRE_NOTHROW(feedhandler.get_feed(4));
	CHECK_THROWS_AS(feedhandler.get_feed(5), std::out_of_range);
	CHECK_THROWS_AS(feedhandler.get_feed(-1), std::out_of_range);
}

TEST_CASE("Returns correct number using get_feed_count_by_tag()",
	"[feedhandler]")
{
	FeedHandler feedhandler;
	std::unique_ptr<configcontainer> cfg(new configcontainer());
	std::unique_ptr<cache> rsscache(new cache(":memory:", cfg.get()));
	feedhandler.set_feeds(get_five_empty_feeds(rsscache.get()));
	feedhandler.get_feed(0)->set_tags({"Chicken", "Horse"});
	feedhandler.get_feed(1)->set_tags({"Horse", "Duck"});
	feedhandler.get_feed(2)->set_tags({"Duck", "Frog"});
	feedhandler.get_feed(3)->set_tags({"Duck", "Hawk"});

	REQUIRE(feedhandler.get_feed_count_per_tag("Ice Cream") == 0);
	REQUIRE(feedhandler.get_feed_count_per_tag("Chicken") == 1);
	REQUIRE(feedhandler.get_feed_count_per_tag("Horse") == 2);
	REQUIRE(feedhandler.get_feed_count_per_tag("Duck") == 3);
}

TEST_CASE("Correctly returns pos of next unread item", "[feedhandler]")
{
	FeedHandler feedhandler;
	std::unique_ptr<configcontainer> cfg(new configcontainer());
	std::unique_ptr<cache> rsscache(new cache(":memory:", cfg.get()));
	const auto feeds = get_five_empty_feeds(rsscache.get());
	int i = 0;
	for (const auto& feed : feeds) {
		const auto item = std::make_shared<rss_item>(rsscache.get());
		if ((i % 2) == 0)
			item->set_unread_nowrite(true);
		else
			item->set_unread_nowrite(false);
		feed->add_item(item);
		i++;
	}
	feedhandler.set_feeds(feeds);

	REQUIRE(feedhandler.get_pos_of_next_unread(0) == 2);
	REQUIRE(feedhandler.get_pos_of_next_unread(2) == 4);
}

TEST_CASE("Correctly sorts feeds", "[feedhandler]")
{
	SECTION("by none asc")
	{
	}

	SECTION("by none desc")
	{
	}

	SECTION("by firsttag asc")
	{
	}

	SECTION("by firsttag desc")
	{
	}

	SECTION("by title asc")
	{
	}

	SECTION("by title desc")
	{
	}

	SECTION("by articlecount asc")
	{
	}

	SECTION("by articlecount desc")
	{
	}

	SECTION("by unreadarticlecount asc")
	{
	}

	SECTION("by unreadarticlecount desc")
	{
	}

	SECTION("by lastupdated asc")
	{
	}

	SECTION("by lastupdated desc")
	{
	}
}

TEST_CASE("mark_all_feed_items_read() marks all of feed's items as read",
	"[feedhandler]")
{
	FeedHandler feedhandler;
	std::unique_ptr<configcontainer> cfg(new configcontainer());
	std::unique_ptr<cache> rsscache(new cache(":memory:", cfg.get()));
	const auto feeds = get_five_empty_feeds(rsscache.get());
	const auto feed = feeds.at(0);
	for (int j = 0; j < 5; ++j) {
		const auto item = std::make_shared<rss_item>(rsscache.get());
		item->set_unread_nowrite(true);
		feed->add_item(item);
	}
	feedhandler.set_feeds(feeds);

	feedhandler.mark_all_feed_items_read(0);

	for (const auto& item : feed->items()) {
		REQUIRE(item->unread() == false);
	}
}

TEST_CASE(
	"reset_feeds_status() changes status of all feeds to \"to be "
	"downloaded\"",
	"[feedhandler]")
{
	FeedHandler feedhandler;
	std::unique_ptr<configcontainer> cfg(new configcontainer());
	std::unique_ptr<cache> rsscache(new cache(":memory:", cfg.get()));
	const auto feeds = get_five_empty_feeds(rsscache.get());
	for (const auto& feed : feeds) {
		feed->set_status(dl_status::DL_ERROR);
	}
	feedhandler.set_feeds(feeds);

	feedhandler.reset_feeds_status();

	for (const auto& feed : feeds) {
		REQUIRE(feed->get_status() == "_");
	}
}

TEST_CASE("clear_feeds_items() clears all of feed's items", "[feedhandler]")
{
	FeedHandler feedhandler;
	std::unique_ptr<configcontainer> cfg(new configcontainer());
	std::unique_ptr<cache> rsscache(new cache(":memory:", cfg.get()));
	feedhandler.set_feeds({});
	const auto feed = std::make_shared<rss_feed>(rsscache.get());
	for (int j = 0; j < 5; ++j) {
		feed->add_item(std::make_shared<rss_item>(rsscache.get()));
	}
	feedhandler.add_feed(feed);

	REQUIRE(feed->items().size() == 5);
	feedhandler.clear_feeds_items();
	REQUIRE(feed->items().size() == 0);
}
<|endoftext|>


<commit_before>/****************************************************************************
* Copyright (c) 2012, Michael P. Gerlek (mpg@flaxen.com)
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following
* conditions are met:
*
*     * Redistributions of source code must retain the above copyright
*       notice, this list of conditions and the following disclaimer.
*     * Redistributions in binary form must reproduce the above copyright
*       notice, this list of conditions and the following disclaimer in
*       the documentation and/or other materials provided
*       with the distribution.
*     * Neither the name of Hobu, Inc. or Flaxen Consulting LLC nor the
*       names of its contributors may be used to endorse or promote
*       products derived from this software without specific prior
*       written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
* OF SUCH DAMAGE.
****************************************************************************/

#include <memory>
#include <vector>

#include "NitfWriter.hpp"

#include <pdal/pdal_macros.hpp>

#include <pdal/PointView.hpp>
#include <pdal/GlobalEnvironment.hpp>

#ifdef PDAL_COMPILER_GCC
#  pragma GCC diagnostic push
#  pragma GCC diagnostic ignored "-Wredundant-decls"
#  pragma GCC diagnostic ignored "-Wextra"
#  pragma GCC diagnostic ignored "-Wcast-qual"
   // The following pragma doesn't actually work:
   //   https://gcc.gnu.org/bugzilla/show_bug.cgi?id=61653
#  pragma GCC diagnostic ignored "-Wliteral-suffix"
#endif
#include <ogr_spatialref.h>
#include <cpl_conv.h>
#ifdef PDAL_COMPILER_CLANG
#  pragma clang diagnostic push
#  pragma clang diagnostic ignored "-Wunused-private-field"
#endif

#define IMPORT_NITRO_API
#include <nitro/c++/import/nitf.hpp>
#include "tre_plugins.hpp"

#ifdef PDAL_COMPILER_CLANG
#  pragma clang diagnostic pop
#endif
#ifdef PDAL_COMPILER_GCC
#  pragma GCC diagnostic pop
#endif

// NOTES
//
// is it legal to write a LAZ file?
// syntactically, how do we name all the LAS writer options that we will pass to the las writer?
//

namespace pdal
{

static PluginInfo const s_info = PluginInfo(
    "writers.nitf",
    "NITF Writer",
    "http://pdal.io/stages/writers.nitf.html" );

CREATE_SHARED_PLUGIN(1, 0, NitfWriter, Writer, s_info)

std::string NitfWriter::getName() const { return s_info.name; }

BOX3D reprojectBoxToDD(const SpatialReference& reference, const BOX3D& box)
{
    if (reference.empty())
        return BOX3D();

    BOX3D output(box);

    OGRSpatialReferenceH current =
        OSRNewSpatialReference(reference.getWKT(SpatialReference::eCompoundOK, false).c_str());
    OGRSpatialReferenceH dd =
        OSRNewSpatialReference(0);

    OGRErr err = OSRSetFromUserInput(dd, const_cast<char *>("EPSG:4326"));
    if (err != OGRERR_NONE)
        throw std::invalid_argument("could not import coordinate system "
            "into OGRSpatialReference SetFromUserInput");

    OGRCoordinateTransformationH transform = OCTNewCoordinateTransformation(current, dd);

    int ret = OCTTransform(transform, 1, &output.minx, &output.miny, &output.minz);
    if (ret == 0)
    {
        std::ostringstream msg;
        msg << "Could not project point for reprojectBoxToDD::min" <<
            CPLGetLastErrorMsg() << ret;
        throw pdal_error(msg.str());
    }

    ret = OCTTransform(transform, 1, &output.maxx, &output.maxy, &output.maxz);
    if (ret == 0)
    {
        std::ostringstream msg;
        msg << "Could not project point for reprojectBoxToDD::max" <<
            CPLGetLastErrorMsg() << ret;
        throw pdal_error(msg.str());
    }

    OCTDestroyCoordinateTransformation(transform);
    OSRDestroySpatialReference(current);
    OSRDestroySpatialReference(dd);

    return output;
}

NitfWriter::NitfWriter() :  LasWriter(&m_oss)
{
    register_tre_plugins();
}

void NitfWriter::processOptions(const Options& options)
{
    LasWriter::processOptions(options);
    m_cLevel = options.getValueOrDefault<std::string>("CLEVEL","03");
    m_sType = options.getValueOrDefault<std::string>("STYPE","BF01");
    m_oStationId = options.getValueOrDefault<std::string>("OSTAID","PDAL");
    m_fileTitle = options.getValueOrDefault<std::string>("FTITLE",m_filename);
    m_fileClass = options.getValueOrDefault<std::string>("FSCLAS","U");
    m_origName = options.getValueOrDefault<std::string>("ONAME","");
    m_origPhone = options.getValueOrDefault<std::string>("OPHONE","");
    m_securityClass = options.getValueOrDefault<std::string>("FSCLAS","U");
    m_securityControlAndHandling = options.getValueOrDefault<std::string>("FSCTLH","");
    m_securityClassificationSystem = options.getValueOrDefault<std::string>("FSCLSY","");
    m_imgSecurityClass = options.getValueOrDefault<std::string>("FSCLAS","U");
    m_imgDate = getOptions().getValueOrDefault<std::string>("IDATIM", "");
    m_imgIdentifier2 = getOptions().getValueOrDefault<std::string>("IID2", "");
    m_sic = getOptions().getValueOrDefault<std::string>("FSCLTX", "");
    m_igeolob = getOptions().getValueOrDefault<std::string>("GEOLOB", "");
    try
    {
        m_aimidb = getOptions().getOption("AIMIDB");
    } catch (pdal::option_not_found&)
    {}
    try
    {
        m_acftb = getOptions().getOption("ACFTB");
    } catch (pdal::option_not_found&)
    {}
}

void NitfWriter::write(const PointViewPtr view)
{
    m_bounds.grow(view->calculateBounds(true));

    LasWriter::write(view);
}

void NitfWriter::done(PointTableRef table)
{
    LasWriter::done(table);

    try
    {
        ::nitf::Record record(NITF_VER_21);
        ::nitf::FileHeader header = record.getHeader();
        header.getFileHeader().set("NITF");
        header.getComplianceLevel().set(m_cLevel);
        header.getSystemType().set(m_sType);
        header.getOriginStationID().set(m_oStationId);
        header.getFileTitle().set(m_fileTitle);
        header.getClassification().set(m_fileClass);
        header.getMessageCopyNum().set("00000");
        header.getMessageNumCopies().set("00000");
        header.getEncrypted().set("0");
        header.getBackgroundColor().setRawData(const_cast<char*>("000"), 3);
        header.getOriginatorName().set(m_origName);
        header.getOriginatorPhone().set(m_origPhone);
        header.getSecurityGroup().getClassificationSystem().set(m_securityClassificationSystem);
        header.getSecurityGroup().getControlAndHandling().set(m_securityControlAndHandling);
        header.getSecurityGroup().getClassificationText().set(m_sic);

        ::nitf::DESegment des = record.newDataExtensionSegment();

        des.getSubheader().getFilePartType().set("DE");
        des.getSubheader().getTypeID().set("LIDARA DES");
        des.getSubheader().getVersion().set("01");
        des.getSubheader().getSecurityClass().set(m_securityClass);
        ::nitf::FileSecurity security = record.getHeader().getSecurityGroup();
        des.getSubheader().setSecurityGroup(security.clone());

        ::nitf::TRE usrHdr("LIDARA DES", "raw_data");
        usrHdr.setField("raw_data", "not");
        ::nitf::Field fld = usrHdr.getField("raw_data");
        fld.setType(::nitf::Field::BINARY);

        flush();
        m_oss.flush();
        std::streambuf *buf = m_oss.rdbuf();
        long size = buf->pubseekoff(0, m_oss.end);
        buf->pubseekoff(0, m_oss.beg);

        std::vector<char> bytes(size);
        buf->sgetn(bytes.data(), size);

        des.getSubheader().setSubheaderFields(usrHdr);

        ::nitf::ImageSegment image = record.newImageSegment();
        ::nitf::ImageSubheader subheader = image.getSubheader();

        BOX3D bounds =  reprojectBoxToDD(table.spatialRef(), m_bounds);

        double corners[4][2];
        corners[0][0] = bounds.maxy;     corners[0][1] = bounds.minx;
        corners[1][0] = bounds.maxy;     corners[1][1] = bounds.maxx;
        corners[2][0] = bounds.miny;  corners[2][1] = bounds.maxx;
        corners[3][0] = bounds.miny;  corners[3][1] = bounds.minx;
        subheader.setCornersFromLatLons(NRT_CORNERS_DECIMAL, corners);

        subheader.getImageSecurityClass().set(m_imgSecurityClass);
        subheader.setSecurityGroup(security.clone());
        if (m_imgDate.size())
            subheader.getImageDateAndTime().set(m_imgDate);

        ::nitf::BandInfo info;
        ::nitf::LookupTable lt(0,0);
        info.init("G",    /* The band representation, Nth band */
                  " ",      /* The band subcategory */
                  "N",      /* The band filter condition */
                  "   ",    /* The band standard image filter code */
                  0,        /* The number of look-up tables */
                  0,        /* The number of entries/LUT */
                  lt);     /* The look-up tables */

        std::vector< ::nitf::BandInfo> bands;
        bands.push_back(info);
        subheader.setPixelInformation(
            "INT",      /* Pixel value type */
            8,         /* Number of bits/pixel */
            8,         /* Actual number of bits/pixel */
            "R",       /* Pixel justification */
            "NODISPLY",     /* Image representation */
            "VIS",     /* Image category */
            1,         /* Number of bands */
            bands);

        subheader.setBlocking(
            8,   /*!< The number of rows */
            8,  /*!< The number of columns */
            8, /*!< The number of rows/block */
            8,  /*!< The number of columns/block */
            "P");                /*!< Image mode */

        //Image Header fields to set
        subheader.getImageId().set("None");
        subheader.getImageTitle().set(m_imgIdentifier2);

        // 64 char string
        std::string zeros(64, '0');

        std::unique_ptr< ::nitf::BandSource> band(new ::nitf::MemorySource(
            const_cast<char*>(zeros.c_str()),
            zeros.size() /* memory size */,
            0 /* starting offset */,
            1 /* bytes per pixel */,
            0 /*skip*/));
        ::nitf::ImageSource iSource;
        iSource.addBand(*band);

        //AIMIDB
        if (!m_aimidb.empty())
        {
            boost::optional<const Options&> options = m_aimidb.getOptions();
            if (options)
            {
                ::nitf::TRE tre("AIMIDB");
                std::vector<Option> opts = options->getOptions();
                for (auto i = opts.begin(); i != opts.end(); ++i)
                {
                    tre.setField(i->getName(), i->getValue<std::string>());
                }
                subheader.getExtendedSection().appendTRE(tre);
            }
        }

        //ACFTB
        if (!m_acftb.empty())
        {
            boost::optional<const Options&> options = m_acftb.getOptions();
            if (options)
            {
                ::nitf::TRE tre("ACFTB");
                std::vector<Option> opts = options->getOptions();
                for (auto i = opts.begin(); i != opts.end(); ++i)
                {
                    tre.setField(i->getName(), i->getValue<std::string>());
                }
                subheader.getExtendedSection().appendTRE(tre);
            }
        }

        ::nitf::Writer writer;
        ::nitf::IOHandle output_io(m_filename.c_str(), NITF_ACCESS_WRITEONLY,
            NITF_CREATE);
        writer.prepare(output_io, record);

        ::nitf::SegmentWriter sWriter = writer.newDEWriter(0);

        ::nitf::SegmentMemorySource sSource(bytes.data(), size, 0, 0, false);
        sWriter.attachSource(sSource);

        ::nitf::ImageWriter iWriter = writer.newImageWriter(0);
        iWriter.attachSource(iSource);

        writer.write();
        output_io.close();
    }
    catch (except::Throwable & t)
    {
        std::ostringstream oss;
        // std::cout << t.getTrace();
        throw pdal_error(t.getMessage());
    }
}

} // namespaces
<commit_msg>apply patch from Todd Murphy to ensure valid boxes for small footprints in IGEOLO in writers.nitf<commit_after>/****************************************************************************
* Copyright (c) 2012, Michael P. Gerlek (mpg@flaxen.com)
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following
* conditions are met:
*
*     * Redistributions of source code must retain the above copyright
*       notice, this list of conditions and the following disclaimer.
*     * Redistributions in binary form must reproduce the above copyright
*       notice, this list of conditions and the following disclaimer in
*       the documentation and/or other materials provided
*       with the distribution.
*     * Neither the name of Hobu, Inc. or Flaxen Consulting LLC nor the
*       names of its contributors may be used to endorse or promote
*       products derived from this software without specific prior
*       written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
* OF SUCH DAMAGE.
****************************************************************************/

#include <memory>
#include <vector>

#include "NitfWriter.hpp"

#include <pdal/pdal_macros.hpp>

#include <pdal/PointView.hpp>
#include <pdal/GlobalEnvironment.hpp>

#ifdef PDAL_COMPILER_GCC
#  pragma GCC diagnostic push
#  pragma GCC diagnostic ignored "-Wredundant-decls"
#  pragma GCC diagnostic ignored "-Wextra"
#  pragma GCC diagnostic ignored "-Wcast-qual"
   // The following pragma doesn't actually work:
   //   https://gcc.gnu.org/bugzilla/show_bug.cgi?id=61653
#  pragma GCC diagnostic ignored "-Wliteral-suffix"
#endif
#include <ogr_spatialref.h>
#include <cpl_conv.h>
#ifdef PDAL_COMPILER_CLANG
#  pragma clang diagnostic push
#  pragma clang diagnostic ignored "-Wunused-private-field"
#endif

#define IMPORT_NITRO_API
#include <nitro/c++/import/nitf.hpp>
#include "tre_plugins.hpp"

#ifdef PDAL_COMPILER_CLANG
#  pragma clang diagnostic pop
#endif
#ifdef PDAL_COMPILER_GCC
#  pragma GCC diagnostic pop
#endif

// NOTES
//
// is it legal to write a LAZ file?
// syntactically, how do we name all the LAS writer options that we will pass to the las writer?
//

namespace pdal
{

static PluginInfo const s_info = PluginInfo(
    "writers.nitf",
    "NITF Writer",
    "http://pdal.io/stages/writers.nitf.html" );

CREATE_SHARED_PLUGIN(1, 0, NitfWriter, Writer, s_info)

std::string NitfWriter::getName() const { return s_info.name; }

BOX3D reprojectBoxToDD(const SpatialReference& reference, const BOX3D& box)
{
    if (reference.empty())
        return BOX3D();

    BOX3D output(box);

    OGRSpatialReferenceH current =
        OSRNewSpatialReference(reference.getWKT(SpatialReference::eCompoundOK, false).c_str());
    OGRSpatialReferenceH dd =
        OSRNewSpatialReference(0);

    OGRErr err = OSRSetFromUserInput(dd, const_cast<char *>("EPSG:4326"));
    if (err != OGRERR_NONE)
        throw std::invalid_argument("could not import coordinate system "
            "into OGRSpatialReference SetFromUserInput");

    OGRCoordinateTransformationH transform = OCTNewCoordinateTransformation(current, dd);

    int ret = OCTTransform(transform, 1, &output.minx, &output.miny, &output.minz);
    if (ret == 0)
    {
        std::ostringstream msg;
        msg << "Could not project point for reprojectBoxToDD::min" <<
            CPLGetLastErrorMsg() << ret;
        throw pdal_error(msg.str());
    }

    ret = OCTTransform(transform, 1, &output.maxx, &output.maxy, &output.maxz);
    if (ret == 0)
    {
        std::ostringstream msg;
        msg << "Could not project point for reprojectBoxToDD::max" <<
            CPLGetLastErrorMsg() << ret;
        throw pdal_error(msg.str());
    }

    OCTDestroyCoordinateTransformation(transform);
    OSRDestroySpatialReference(current);
    OSRDestroySpatialReference(dd);

    return output;
}

NitfWriter::NitfWriter() :  LasWriter(&m_oss)
{
    register_tre_plugins();
}

void NitfWriter::processOptions(const Options& options)
{
    LasWriter::processOptions(options);
    m_cLevel = options.getValueOrDefault<std::string>("CLEVEL","03");
    m_sType = options.getValueOrDefault<std::string>("STYPE","BF01");
    m_oStationId = options.getValueOrDefault<std::string>("OSTAID","PDAL");
    m_fileTitle = options.getValueOrDefault<std::string>("FTITLE",m_filename);
    m_fileClass = options.getValueOrDefault<std::string>("FSCLAS","U");
    m_origName = options.getValueOrDefault<std::string>("ONAME","");
    m_origPhone = options.getValueOrDefault<std::string>("OPHONE","");
    m_securityClass = options.getValueOrDefault<std::string>("FSCLAS","U");
    m_securityControlAndHandling = options.getValueOrDefault<std::string>("FSCTLH","");
    m_securityClassificationSystem = options.getValueOrDefault<std::string>("FSCLSY","");
    m_imgSecurityClass = options.getValueOrDefault<std::string>("FSCLAS","U");
    m_imgDate = getOptions().getValueOrDefault<std::string>("IDATIM", "");
    m_imgIdentifier2 = getOptions().getValueOrDefault<std::string>("IID2", "");
    m_sic = getOptions().getValueOrDefault<std::string>("FSCLTX", "");
    m_igeolob = getOptions().getValueOrDefault<std::string>("GEOLOB", "");
    try
    {
        m_aimidb = getOptions().getOption("AIMIDB");
    } catch (pdal::option_not_found&)
    {}
    try
    {
        m_acftb = getOptions().getOption("ACFTB");
    } catch (pdal::option_not_found&)
    {}
}

void NitfWriter::write(const PointViewPtr view)
{
    m_bounds.grow(view->calculateBounds(true));

    LasWriter::write(view);
}

void NitfWriter::done(PointTableRef table)
{
    LasWriter::done(table);

    try
    {
        ::nitf::Record record(NITF_VER_21);
        ::nitf::FileHeader header = record.getHeader();
        header.getFileHeader().set("NITF");
        header.getComplianceLevel().set(m_cLevel);
        header.getSystemType().set(m_sType);
        header.getOriginStationID().set(m_oStationId);
        header.getFileTitle().set(m_fileTitle);
        header.getClassification().set(m_fileClass);
        header.getMessageCopyNum().set("00000");
        header.getMessageNumCopies().set("00000");
        header.getEncrypted().set("0");
        header.getBackgroundColor().setRawData(const_cast<char*>("000"), 3);
        header.getOriginatorName().set(m_origName);
        header.getOriginatorPhone().set(m_origPhone);
        header.getSecurityGroup().getClassificationSystem().set(m_securityClassificationSystem);
        header.getSecurityGroup().getControlAndHandling().set(m_securityControlAndHandling);
        header.getSecurityGroup().getClassificationText().set(m_sic);

        ::nitf::DESegment des = record.newDataExtensionSegment();

        des.getSubheader().getFilePartType().set("DE");
        des.getSubheader().getTypeID().set("LIDARA DES");
        des.getSubheader().getVersion().set("01");
        des.getSubheader().getSecurityClass().set(m_securityClass);
        ::nitf::FileSecurity security = record.getHeader().getSecurityGroup();
        des.getSubheader().setSecurityGroup(security.clone());

        ::nitf::TRE usrHdr("LIDARA DES", "raw_data");
        usrHdr.setField("raw_data", "not");
        ::nitf::Field fld = usrHdr.getField("raw_data");
        fld.setType(::nitf::Field::BINARY);

        flush();
        m_oss.flush();
        std::streambuf *buf = m_oss.rdbuf();
        long size = buf->pubseekoff(0, m_oss.end);
        buf->pubseekoff(0, m_oss.beg);

        std::vector<char> bytes(size);
        buf->sgetn(bytes.data(), size);

        des.getSubheader().setSubheaderFields(usrHdr);

        ::nitf::ImageSegment image = record.newImageSegment();
        ::nitf::ImageSubheader subheader = image.getSubheader();

        BOX3D bounds =  reprojectBoxToDD(table.spatialRef(), m_bounds);

        //NITF decimal degree values for corner coordinates only has a precision of 3 after the decimal
        //This may cause an invalid polygon due to rounding errors with a small tile. Therefore instead of rounding
        //min values will use the floor value and max values will use the ceiling values.
        bounds.minx=(floor(bounds.minx*1000))/1000.0;
        bounds.miny=(floor(bounds.miny*1000))/1000.0;
        bounds.maxx=(ceil(bounds.maxx*1000))/1000.0;
        bounds.maxy=(ceil(bounds.maxy*1000))/1000.0;

        double corners[4][2];
        corners[0][0] = bounds.maxy;     corners[0][1] = bounds.minx;
        corners[1][0] = bounds.maxy;     corners[1][1] = bounds.maxx;
        corners[2][0] = bounds.miny;  corners[2][1] = bounds.maxx;
        corners[3][0] = bounds.miny;  corners[3][1] = bounds.minx;
        subheader.setCornersFromLatLons(NRT_CORNERS_DECIMAL, corners);

        subheader.getImageSecurityClass().set(m_imgSecurityClass);
        subheader.setSecurityGroup(security.clone());
        if (m_imgDate.size())
            subheader.getImageDateAndTime().set(m_imgDate);

        ::nitf::BandInfo info;
        ::nitf::LookupTable lt(0,0);
        info.init("G",    /* The band representation, Nth band */
                  " ",      /* The band subcategory */
                  "N",      /* The band filter condition */
                  "   ",    /* The band standard image filter code */
                  0,        /* The number of look-up tables */
                  0,        /* The number of entries/LUT */
                  lt);     /* The look-up tables */

        std::vector< ::nitf::BandInfo> bands;
        bands.push_back(info);
        subheader.setPixelInformation(
            "INT",      /* Pixel value type */
            8,         /* Number of bits/pixel */
            8,         /* Actual number of bits/pixel */
            "R",       /* Pixel justification */
            "NODISPLY",     /* Image representation */
            "VIS",     /* Image category */
            1,         /* Number of bands */
            bands);

        subheader.setBlocking(
            8,   /*!< The number of rows */
            8,  /*!< The number of columns */
            8, /*!< The number of rows/block */
            8,  /*!< The number of columns/block */
            "P");                /*!< Image mode */

        //Image Header fields to set
        subheader.getImageId().set("None");
        subheader.getImageTitle().set(m_imgIdentifier2);

        // 64 char string
        std::string zeros(64, '0');

        std::unique_ptr< ::nitf::BandSource> band(new ::nitf::MemorySource(
            const_cast<char*>(zeros.c_str()),
            zeros.size() /* memory size */,
            0 /* starting offset */,
            1 /* bytes per pixel */,
            0 /*skip*/));
        ::nitf::ImageSource iSource;
        iSource.addBand(*band);

        //AIMIDB
        if (!m_aimidb.empty())
        {
            boost::optional<const Options&> options = m_aimidb.getOptions();
            if (options)
            {
                ::nitf::TRE tre("AIMIDB");
                std::vector<Option> opts = options->getOptions();
                for (auto i = opts.begin(); i != opts.end(); ++i)
                {
                    tre.setField(i->getName(), i->getValue<std::string>());
                }
                subheader.getExtendedSection().appendTRE(tre);
            }
        }

        //ACFTB
        if (!m_acftb.empty())
        {
            boost::optional<const Options&> options = m_acftb.getOptions();
            if (options)
            {
                ::nitf::TRE tre("ACFTB");
                std::vector<Option> opts = options->getOptions();
                for (auto i = opts.begin(); i != opts.end(); ++i)
                {
                    tre.setField(i->getName(), i->getValue<std::string>());
                }
                subheader.getExtendedSection().appendTRE(tre);
            }
        }

        ::nitf::Writer writer;
        ::nitf::IOHandle output_io(m_filename.c_str(), NITF_ACCESS_WRITEONLY,
            NITF_CREATE);
        writer.prepare(output_io, record);

        ::nitf::SegmentWriter sWriter = writer.newDEWriter(0);

        ::nitf::SegmentMemorySource sSource(bytes.data(), size, 0, 0, false);
        sWriter.attachSource(sSource);

        ::nitf::ImageWriter iWriter = writer.newImageWriter(0);
        iWriter.attachSource(iSource);

        writer.write();
        output_io.close();
    }
    catch (except::Throwable & t)
    {
        std::ostringstream oss;
        // std::cout << t.getTrace();
        throw pdal_error(t.getMessage());
    }
}

} // namespaces
<|endoftext|>


<commit_before>/******************************************************************************
 * This file is part of the Mula project
 * Copyright (c) 2011 Laszlo Papp <lpapp@kde.org>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 */

#include "indexfile.h"

#include "file.h"
#include "wordentry.h"

#include <QtCore/QDebug>
#include <QtCore/QStringList>
#include <QtCore/QFile>

#include <arpa/inet.h>

using namespace MulaPluginStarDict;

class IndexFile::Private
{
    public:
        Private()
        {
        }

        ~Private()
        {
        }

        QList<WordEntry> wordEntryList;
};

IndexFile::IndexFile()
    : d(new Private)
{
}

IndexFile::~IndexFile()
{
}

bool
IndexFile::load(const QString& filePath, long wc, qulonglong fileSize)
{
    QFile file(filePath);
    if (!file.open(QIODevice::ReadOnly))
    {
        qDebug() << Q_FUNC_INFO << "Failed to open file:" << filePath;
        return -1;
    }

    QByteArray indexDataBuffer = file.read(fileSize);

    file.close();

    int position = 0;
    for (int i = 0; i < wc; ++i)
    {
        WordEntry wordEntry;
        wordEntry.setData(indexDataBuffer.mid(position));
        ++position;
        wordEntry.setDataOffset(ntohl(*reinterpret_cast<quint32 *>(indexDataBuffer.mid(position).data())));
        position += sizeof(quint32);
        wordEntry.setDataSize(ntohl(*reinterpret_cast<quint32 *>(indexDataBuffer.mid(position).data())));
        position += sizeof(quint32);

        d->wordEntryList.append(wordEntry);
    }

    return true;
}

QByteArray
IndexFile::key(long index)
{
    return d->wordEntryList.at(index).data();
}

bool
IndexFile::lookup(const QByteArray &word, long &index)
{
    bool found = false;
    int indexTo = d->wordEntryList.size() - 1;

    if (stardictStringCompare(word, d->wordEntryList.first().data()) < 0)
    {
        index = 0;
    }
    else if (stardictStringCompare(word, key(indexTo)) > 0)
    {
        index = invalidIndex;
    }
    else
    {
        int indexThisIndex = 0;
        int indexFrom = 0;
        int cmpint;
        while (indexFrom <= indexTo)
        {
            indexThisIndex = (indexFrom + indexTo) / 2;
            cmpint = stardictStringCompare(word, key(indexThisIndex));
            if (cmpint > 0)
            {
                indexFrom = indexThisIndex + 1;
            }
            else if (cmpint < 0)
            {
                indexTo = indexThisIndex - 1;
            }
            else
            {
                found = true;
                break;
            }
        }

        if (!found)
            index = indexFrom;    //next
        else
            index = indexThisIndex;
    }

    return found;
}

quint32
IndexFile::wordEntryOffset() const
{
    return AbstractIndexFile::wordEntryOffset();
}

void
IndexFile::setWordEntryOffset(quint32 offset)
{
    AbstractIndexFile::setWordEntryOffset(offset);
}

quint32
IndexFile::wordEntrySize() const
{
    return AbstractIndexFile::wordEntrySize();
}

void
IndexFile::setWordEntrySize(quint32 size)
{
    AbstractIndexFile::setWordEntrySize(size);
}
<commit_msg>Use qBinaryFind instead of reimplementing the internals of that algorithm<commit_after>/******************************************************************************
 * This file is part of the Mula project
 * Copyright (c) 2011 Laszlo Papp <lpapp@kde.org>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 */

#include "indexfile.h"

#include "file.h"
#include "wordentry.h"

#include <QtCore/QDebug>
#include <QtCore/QStringList>
#include <QtCore/QFile>

#include <arpa/inet.h>

using namespace MulaPluginStarDict;

class IndexFile::Private
{
    public:
        Private()
        {
        }

        ~Private()
        {
        }

        QList<WordEntry> wordEntryList;
};

IndexFile::IndexFile()
    : d(new Private)
{
}

IndexFile::~IndexFile()
{
}

bool
IndexFile::load(const QString& filePath, long wc, qulonglong fileSize)
{
    QFile file(filePath);
    if (!file.open(QIODevice::ReadOnly))
    {
        qDebug() << Q_FUNC_INFO << "Failed to open file:" << filePath;
        return -1;
    }

    QByteArray indexDataBuffer = file.read(fileSize);

    file.close();

    int position = 0;
    for (int i = 0; i < wc; ++i)
    {
        WordEntry wordEntry;
        wordEntry.setData(indexDataBuffer.mid(position));
        ++position;
        wordEntry.setDataOffset(ntohl(*reinterpret_cast<quint32 *>(indexDataBuffer.mid(position).data())));
        position += sizeof(quint32);
        wordEntry.setDataSize(ntohl(*reinterpret_cast<quint32 *>(indexDataBuffer.mid(position).data())));
        position += sizeof(quint32);

        d->wordEntryList.append(wordEntry);
    }

    return true;
}

QByteArray
IndexFile::key(long index)
{
    return d->wordEntryList.at(index).data();
}

inline bool
lessThanCompare(const QString string1, const QString string2)
{
    return stardictStringCompare(string1, string2) < 0;
}

bool
IndexFile::lookup(const QByteArray &word, long &index)
{
    QStringList wordList;
    foreach (const WordEntry &wordEntry, d->wordEntryList)
        wordList.append(QString::fromUtf8(wordEntry.data()));

    QStringList::iterator i = qBinaryFind(wordList.begin(), wordList.end(), QString::fromUtf8(word), lessThanCompare);

    if (i == wordList.end())
        index = -1;
    else
        index = i - wordList.begin();

    return true;
}

quint32
IndexFile::wordEntryOffset() const
{
    return AbstractIndexFile::wordEntryOffset();
}

void
IndexFile::setWordEntryOffset(quint32 offset)
{
    AbstractIndexFile::setWordEntryOffset(offset);
}

quint32
IndexFile::wordEntrySize() const
{
    return AbstractIndexFile::wordEntrySize();
}

void
IndexFile::setWordEntrySize(quint32 size)
{
    AbstractIndexFile::setWordEntrySize(size);
}
<|endoftext|>


<commit_before>// RUN: %clangxx_msan -O0 %s -o %t && %run %t
//
// AArch64 shows fails with uninitialized bytes in __interceptor_strcmp from
// dlfcn/dlerror.c:107 (glibc).
// XFAIL: aarch64

#include <assert.h>
#include <dlfcn.h>
#include <stdio.h>
#include <string.h>

int main(void) {
  void *p = dlopen("/bad/file/name", RTLD_NOW);
  assert(!p);
  char *s = dlerror();
  printf("%s, %zu\n", s, strlen(s));
  return 0;
}
<commit_msg>[MSan] Mark dlerror.cc expected failure for MIPS<commit_after>// RUN: %clangxx_msan -O0 %s -o %t && %run %t
//
// AArch64, MIPS64 shows fails with uninitialized bytes in __interceptor_strcmp from
// dlfcn/dlerror.c:107 (glibc).
// XFAIL: aarch64
// XFAIL: mips64

#include <assert.h>
#include <dlfcn.h>
#include <stdio.h>
#include <string.h>

int main(void) {
  void *p = dlopen("/bad/file/name", RTLD_NOW);
  assert(!p);
  char *s = dlerror();
  printf("%s, %zu\n", s, strlen(s));
  return 0;
}
<|endoftext|>


<commit_before>// This file is part of Eigen, a lightweight C++ template library
// for linear algebra.
//
// Copyright (C) 2010 Hauke Heibel <hauke.heibel@gmail.com>
//
// Eigen is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 3 of the License, or (at your option) any later version.
//
// Alternatively, you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
// published by the Free Software Foundation; either version 2 of
// the License, or (at your option) any later version.
//
// Eigen is distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
// FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License or the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License and a copy of the GNU General Public License along with
// Eigen. If not, see <http://www.gnu.org/licenses/>.

#include "main.h"

template <typename MatrixType> void run_nesting_ops(const MatrixType& m)
{
  typedef typename MatrixType::Scalar Scalar;

#ifdef NDEBUG
  const bool is_debug = false;
#else
  const bool is_debug = true;
#endif

  // Make really sure that we are in debug mode! We don't want any type of
  // inlining for these tests to pass.
  VERIFY(is_debug);

  // The only intention of these tests is to ensure that this code does
  // not trigger any asserts or segmentation faults... more to come.
  VERIFY( (m.transpose() * m).diagonal().sum() == (m.transpose() * m).diagonal().sum() );
  VERIFY( (m.transpose() * m).diagonal().array().abs().sum() == (m.transpose() * m).diagonal().array().abs().sum() );
}

void test_nesting_ops()
{
  CALL_SUBTEST_1(run_nesting_ops(MatrixXf::Random(25,25)));
  CALL_SUBTEST_2(run_nesting_ops(MatrixXd::Random(25,25)));
  CALL_SUBTEST_3(run_nesting_ops(Matrix4f::Random()));
  CALL_SUBTEST_4(run_nesting_ops(Matrix4d::Random()));
}
<commit_msg>Added a non-diagonal product nesting test.<commit_after>// This file is part of Eigen, a lightweight C++ template library
// for linear algebra.
//
// Copyright (C) 2010 Hauke Heibel <hauke.heibel@gmail.com>
//
// Eigen is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 3 of the License, or (at your option) any later version.
//
// Alternatively, you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
// published by the Free Software Foundation; either version 2 of
// the License, or (at your option) any later version.
//
// Eigen is distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
// FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License or the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License and a copy of the GNU General Public License along with
// Eigen. If not, see <http://www.gnu.org/licenses/>.

#include "main.h"

template <typename MatrixType> void run_nesting_ops(const MatrixType& m)
{
  typedef typename MatrixType::Scalar Scalar;

#ifdef NDEBUG
  const bool is_debug = false;
#else
  const bool is_debug = true;
#endif

  // Make really sure that we are in debug mode! We don't want any type of
  // inlining for these tests to pass.
  VERIFY(is_debug);

  // The only intention of these tests is to ensure that this code does
  // not trigger any asserts or segmentation faults... more to come.
  VERIFY( (m.transpose() * m).diagonal().sum() == (m.transpose() * m).diagonal().sum() );
  VERIFY( (m.transpose() * m).diagonal().array().abs().sum() == (m.transpose() * m).diagonal().array().abs().sum() );

  VERIFY( (m.transpose() * m).array().abs().sum() == (m.transpose() * m).array().abs().sum() );
}

void test_nesting_ops()
{
  CALL_SUBTEST_1(run_nesting_ops(MatrixXf::Random(25,25)));
  CALL_SUBTEST_2(run_nesting_ops(MatrixXd::Random(25,25)));
  CALL_SUBTEST_3(run_nesting_ops(Matrix4f::Random()));
  CALL_SUBTEST_4(run_nesting_ops(Matrix4d::Random()));
}
<|endoftext|>