<commit_before>/*
 * Vulkan Samples Kit
 *
 * Copyright (C) 2015 LunarG, Inc.
 *
 * 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.
 */

/*
VULKAN_SAMPLE_SHORT_DESCRIPTION
Copy/blit image
*/

#include <util_init.hpp>
#include <assert.h>
#include <string.h>
#include <cstdlib>

int main(int argc, char **argv)
{
    VkResult U_ASSERT_ONLY res;
    struct sample_info info = {};
    char sample_title[] = "Copy/Blit Image";
    VkImageCreateInfo image_info;
    VkImage bltSrcImage;
    VkImage bltDstImage;
    VkMemoryRequirements memReq;
    VkMemoryAllocInfo memAllocInfo;
    VkDeviceMemory dmem;
    unsigned int *pImgMem;

    init_global_layer_properties(info);
    info.instance_extension_names.push_back(VK_EXT_KHR_SWAPCHAIN_EXTENSION_NAME);
    info.device_extension_names.push_back(VK_EXT_KHR_DEVICE_SWAPCHAIN_EXTENSION_NAME);
    init_instance(info, sample_title);
    init_enumerate_device(info);
    init_device(info);
    info.width = info.height = 500;
    init_connection(info);
    init_window(info);
    init_swapchain_extension(info);
    init_command_pool(info);
    init_command_buffer(info);
    init_device_queue(info);
    init_swap_chain(info);
    init_uniform_buffer(info);
    init_descriptor_and_pipeline_layouts(info, false);
    init_renderpass(info, false);
    init_framebuffers(info, false);
    init_descriptor_pool(info, false);
    init_descriptor_set(info, false);

    /* VULKAN_KEY_START */

    VkSemaphore presentCompleteSemaphore;
    VkSemaphoreCreateInfo presentCompleteSemaphoreCreateInfo;
    presentCompleteSemaphoreCreateInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
    presentCompleteSemaphoreCreateInfo.pNext = NULL;
    presentCompleteSemaphoreCreateInfo.flags = VK_FENCE_CREATE_SIGNALED_BIT;

    res = vkCreateSemaphore(info.device,
                            &presentCompleteSemaphoreCreateInfo,
                            &presentCompleteSemaphore);
    assert(res == VK_SUCCESS);

    // Get the index of the next available swapchain image:
    res = info.fpAcquireNextImageKHR(info.device, info.swap_chain,
                                      UINT64_MAX,
                                      presentCompleteSemaphore,
                                      &info.current_buffer);
    // TODO: Deal with the VK_SUBOPTIMAL_KHR and VK_ERROR_OUT_OF_DATE_KHR
    // return codes
    assert(res == VK_SUCCESS);

    // Create an image, map it, and write some values to the image

    image_info.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
    image_info.pNext = NULL;
    image_info.imageType = VK_IMAGE_TYPE_2D;
    image_info.format = info.format;
    image_info.extent.width = info.width;
    image_info.extent.height = info.height;
    image_info.extent.depth = 1;
    image_info.mipLevels = 1;
    image_info.arraySize = 1;
    image_info.samples = NUM_SAMPLES;
    image_info.queueFamilyCount = 0;
    image_info.pQueueFamilyIndices = NULL;
    image_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
    image_info.usage = VK_IMAGE_USAGE_TRANSFER_SOURCE_BIT;
    image_info.flags = 0;
    image_info.tiling = VK_IMAGE_TILING_LINEAR;
    res = vkCreateImage(info.device, &image_info, &bltSrcImage);

    memAllocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOC_INFO;
    memAllocInfo.pNext = NULL;

    res = vkGetImageMemoryRequirements(info.device, bltSrcImage, &memReq);
    res = memory_type_from_properties(info,
                                      memReq.memoryTypeBits,
                                      VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT,
                                      &memAllocInfo.memoryTypeIndex);
    memAllocInfo.allocationSize = memReq.size;
    res = vkAllocMemory(info.device, &memAllocInfo, &dmem);
    res = vkBindImageMemory(info.device, bltSrcImage, dmem, 0);
    res = vkMapMemory(info.device, dmem, 0,0,0,(void **)&pImgMem);
    for (int k = 0; k < memReq.size/4; k++) *pImgMem++ = 0xff0000ff;

    // Flush the mapped memory and then unmap it
    VkMappedMemoryRange memRange;
    memRange.sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE;
    memRange.pNext = NULL;
    memRange.mem = dmem;
    memRange.offset = 0;
    memRange.size = memReq.size;
    res = vkFlushMappedMemoryRanges(info.device, 1, &memRange);
    vkUnmapMemory(info.device, dmem);

    bltDstImage = info.buffers[info.current_buffer].image;

    vkCmdBindDescriptorSets(info.cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, info.pipeline_layout,
            0, NUM_DESCRIPTOR_SETS, info.desc_set.data(), 0, NULL);

    execute_begin_command_buffer(info);

    // Do a image copy to part of the dst image
    VkImageCopy cregion;
    cregion.srcSubresource.aspect = VK_IMAGE_ASPECT_COLOR;
    cregion.srcSubresource.mipLevel = 0;
    cregion.srcSubresource.arrayLayer = 0;
    cregion.srcSubresource.arraySize = 1;
    cregion.srcOffset.x = 0;
    cregion.srcOffset.y = 0;
    cregion.srcOffset.z = 0;
    cregion.destSubresource.aspect = VK_IMAGE_ASPECT_COLOR;
    cregion.destSubresource.mipLevel = 0;
    cregion.destSubresource.arrayLayer = 0;
    cregion.destSubresource.arraySize = 1;
    cregion.destOffset.x = 0;
    cregion.destOffset.y = 0;
    cregion.destOffset.z = 0;
    cregion.extent.width = info.width/2;
    cregion.extent.height = info.height/2;
    cregion.extent.depth = 1;

	vkCmdCopyImage(info.cmd, bltSrcImage, VK_IMAGE_LAYOUT_TRANSFER_SOURCE_OPTIMAL, bltDstImage,
          VK_IMAGE_LAYOUT_TRANSFER_DESTINATION_OPTIMAL, 1, &cregion);

    // Do a blit to all of the dst image
	VkImageBlit region;
    region.srcSubresource.aspect = VK_IMAGE_ASPECT_COLOR;
    region.srcSubresource.mipLevel = 0;
    region.srcSubresource.arrayLayer = 0;
    region.srcSubresource.arraySize = 1;
    region.srcOffset.x = 0;
    region.srcOffset.y = 0;
    region.srcOffset.z = 0;
    region.srcExtent.width = info.width;
    region.srcExtent.height = info.height;
    region.srcExtent.depth = 1;
    region.destSubresource.aspect = VK_IMAGE_ASPECT_COLOR;
    region.destSubresource.mipLevel = 0;
    region.destSubresource.arrayLayer = 0;
    region.destSubresource.arraySize = 1;
    region.destOffset.x = 0;
    region.destOffset.y = 0;
    region.destOffset.z = 0;
    region.destExtent.width = info.width;
    region.destExtent.height = info.height;
    region.destExtent.depth = 1;

	vkCmdBlitImage(info.cmd, bltSrcImage, VK_IMAGE_LAYOUT_TRANSFER_SOURCE_OPTIMAL, bltDstImage,
        VK_IMAGE_LAYOUT_TRANSFER_DESTINATION_OPTIMAL, 1, &region, VK_TEX_FILTER_LINEAR);

    VkImageMemoryBarrier prePresentBarrier = {};
    prePresentBarrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
    prePresentBarrier.pNext = NULL;
    prePresentBarrier.outputMask = VK_MEMORY_OUTPUT_COLOR_ATTACHMENT_BIT;
    prePresentBarrier.inputMask = 0;
    prePresentBarrier.oldLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
    prePresentBarrier.newLayout = VK_IMAGE_LAYOUT_PRESENT_SOURCE_KHR;
    prePresentBarrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
    prePresentBarrier.destQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
    prePresentBarrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
    prePresentBarrier.subresourceRange.baseMipLevel = 0;
    prePresentBarrier.subresourceRange.mipLevels = 1;
    prePresentBarrier.subresourceRange.baseArrayLayer = 0;
    prePresentBarrier.subresourceRange.arraySize = 1;
    prePresentBarrier.image = info.buffers[info.current_buffer].image;
    VkImageMemoryBarrier *pmemory_barrier = &prePresentBarrier;
    vkCmdPipelineBarrier(info.cmd, VK_PIPELINE_STAGE_ALL_GPU_COMMANDS, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
                         VK_FALSE, 1, (const void * const*)&pmemory_barrier);

    res = vkEndCommandBuffer(info.cmd);
    const VkCmdBuffer cmd_bufs[] = { info.cmd };
    VkFence nullFence = { VK_NULL_HANDLE };

    /* Make sure buffer is ready for rendering */
    res = vkQueueWaitSemaphore(info.queue, presentCompleteSemaphore);
    assert(res == VK_SUCCESS);

    /* Queue the command buffer for execution */
    res = vkQueueSubmit(info.queue, 1, cmd_bufs, nullFence);
    assert(res == VK_SUCCESS);

    /* Now present the image in the window */

    VkPresentInfoKHR present;
    present.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR;
    present.pNext = NULL;
    present.swapchainCount = 1;
    present.swapchains = &info.swap_chain;
    present.imageIndices = &info.current_buffer;

    res = info.fpQueuePresentKHR(info.queue, &present);
    assert(res == VK_SUCCESS);

    wait_seconds(1);
    /* VULKAN_KEY_END */

    vkDestroySemaphore(info.device, presentCompleteSemaphore);
    destroy_descriptor_pool(info);
    destroy_framebuffers(info);
    destroy_descriptor_and_pipeline_layouts(info);
    destroy_uniform_buffer(info);
    destroy_swap_chain(info);
    destroy_command_buffer(info);
    destroy_command_pool(info);
    destroy_window(info);
    destroy_device(info);
    destroy_instance(info);
}
<commit_msg>Bring CopyBlitImage sample forward to header 203<commit_after>/*
 * Vulkan Samples Kit
 *
 * Copyright (C) 2015 LunarG, Inc.
 *
 * 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.
 */

/*
VULKAN_SAMPLE_SHORT_DESCRIPTION
Copy/blit image
*/

#include <util_init.hpp>
#include <assert.h>
#include <string.h>
#include <cstdlib>

int main(int argc, char **argv)
{
    VkResult U_ASSERT_ONLY res;
    struct sample_info info = {};
    char sample_title[] = "Copy/Blit Image";
    VkImageCreateInfo image_info;
    VkImage bltSrcImage;
    VkImage bltDstImage;
    VkMemoryRequirements memReq;
    VkMemoryAllocateInfo memAllocInfo;
    VkDeviceMemory dmem;
    unsigned int *pImgMem;

    init_global_layer_properties(info);
    info.instance_extension_names.push_back(VK_EXT_KHR_SWAPCHAIN_EXTENSION_NAME);
    info.device_extension_names.push_back(VK_EXT_KHR_DEVICE_SWAPCHAIN_EXTENSION_NAME);
    init_instance(info, sample_title);
    init_enumerate_device(info);
    init_device(info);
    info.width = info.height = 500;
    init_connection(info);
    init_window(info);
    init_swapchain_extension(info);
    init_command_pool(info);
    init_command_buffer(info);
    init_device_queue(info);
    init_swap_chain(info);
    init_uniform_buffer(info);
    init_descriptor_and_pipeline_layouts(info, false);
    init_renderpass(info, false);
    init_framebuffers(info, false);
    init_descriptor_pool(info, false);
    init_descriptor_set(info, false);

    /* VULKAN_KEY_START */

    VkSemaphore presentCompleteSemaphore;
    VkSemaphoreCreateInfo presentCompleteSemaphoreCreateInfo;
    presentCompleteSemaphoreCreateInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
    presentCompleteSemaphoreCreateInfo.pNext = NULL;
    presentCompleteSemaphoreCreateInfo.flags = VK_FENCE_CREATE_SIGNALED_BIT;

    res = vkCreateSemaphore(info.device,
                            &presentCompleteSemaphoreCreateInfo, NULL,
                            &presentCompleteSemaphore);
    assert(res == VK_SUCCESS);

    // Get the index of the next available swapchain image:
    res = info.fpAcquireNextImageKHR(info.device, info.swap_chain,
                                      UINT64_MAX,
                                      presentCompleteSemaphore,
                                      &info.current_buffer);
    // TODO: Deal with the VK_SUBOPTIMAL_KHR and VK_ERROR_OUT_OF_DATE_KHR
    // return codes
    assert(res == VK_SUCCESS);

    // Create an image, map it, and write some values to the image

    image_info.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
    image_info.pNext = NULL;
    image_info.imageType = VK_IMAGE_TYPE_2D;
    image_info.format = info.format;
    image_info.extent.width = info.width;
    image_info.extent.height = info.height;
    image_info.extent.depth = 1;
    image_info.mipLevels = 1;
    image_info.arrayLayers = 1;
    image_info.samples = NUM_SAMPLES;
    image_info.queueFamilyIndexCount = 0;
    image_info.pQueueFamilyIndices = NULL;
    image_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
    image_info.usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
    image_info.flags = 0;
    image_info.tiling = VK_IMAGE_TILING_LINEAR;
    res = vkCreateImage(info.device, &image_info, NULL, &bltSrcImage);

    memAllocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOC_INFO;
    memAllocInfo.pNext = NULL;

    vkGetImageMemoryRequirements(info.device, bltSrcImage, &memReq);
    bool pass = memory_type_from_properties(info,
                                      memReq.memoryTypeBits,
                                      VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT,
                                      &memAllocInfo.memoryTypeIndex);
    assert(pass);
    memAllocInfo.allocationSize = memReq.size;
    res = vkAllocateMemory(info.device, &memAllocInfo, NULL, &dmem);
    res = vkBindImageMemory(info.device, bltSrcImage, dmem, 0);
    res = vkMapMemory(info.device, dmem, 0,0,0,(void **)&pImgMem);
    for (int k = 0; k < memReq.size/4; k++) *pImgMem++ = 0xff0000ff;

    // Flush the mapped memory and then unmap it
    VkMappedMemoryRange memRange;
    memRange.sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE;
    memRange.pNext = NULL;
    memRange.memory = dmem;
    memRange.offset = 0;
    memRange.size = memReq.size;
    res = vkFlushMappedMemoryRanges(info.device, 1, &memRange);
    vkUnmapMemory(info.device, dmem);

    bltDstImage = info.buffers[info.current_buffer].image;

    vkCmdBindDescriptorSets(info.cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, info.pipeline_layout,
            0, NUM_DESCRIPTOR_SETS, info.desc_set.data(), 0, NULL);

    execute_begin_command_buffer(info);

    // Do a image copy to part of the dst image
    VkImageCopy cregion;
    cregion.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
    cregion.srcSubresource.mipLevel = 0;
    cregion.srcSubresource.baseArrayLayer = 0;
    cregion.srcSubresource.layerCount = 1;
    cregion.srcOffset.x = 0;
    cregion.srcOffset.y = 0;
    cregion.srcOffset.z = 0;
    cregion.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
    cregion.dstSubresource.mipLevel = 0;
    cregion.dstSubresource.baseArrayLayer = 0;
    cregion.dstSubresource.layerCount = 1;
    cregion.dstOffset.x = 0;
    cregion.dstOffset.y = 0;
    cregion.dstOffset.z = 0;
    cregion.extent.width = info.width/2;
    cregion.extent.height = info.height/2;
    cregion.extent.depth = 1;

        vkCmdCopyImage(info.cmd, bltSrcImage, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, bltDstImage,
          VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &cregion);

    // Do a blit to all of the dst image
	VkImageBlit region;
    region.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
    region.srcSubresource.mipLevel = 0;
    region.srcSubresource.baseArrayLayer = 0;
    region.srcSubresource.layerCount = 1;
    region.srcOffset.x = 0;
    region.srcOffset.y = 0;
    region.srcOffset.z = 0;
    region.srcExtent.width = info.width;
    region.srcExtent.height = info.height;
    region.srcExtent.depth = 1;
    region.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
    region.dstSubresource.mipLevel = 0;
    region.dstSubresource.baseArrayLayer = 0;
    region.dstSubresource.layerCount = 1;
    region.dstOffset.x = 0;
    region.dstOffset.y = 0;
    region.dstOffset.z = 0;
    region.dstExtent.width = info.width;
    region.dstExtent.height = info.height;
    region.dstExtent.depth = 1;

        vkCmdBlitImage(info.cmd, bltSrcImage, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, bltDstImage,
        VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &region, VK_FILTER_LINEAR);

    VkImageMemoryBarrier prePresentBarrier = {};
    prePresentBarrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
    prePresentBarrier.pNext = NULL;
    prePresentBarrier.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
    prePresentBarrier.dstAccessMask = 0;
    prePresentBarrier.oldLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
    prePresentBarrier.newLayout = VK_IMAGE_LAYOUT_PRESENT_SOURCE_KHR;
    prePresentBarrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
    prePresentBarrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
    prePresentBarrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
    prePresentBarrier.subresourceRange.baseMipLevel = 0;
    prePresentBarrier.subresourceRange.levelCount = 1;
    prePresentBarrier.subresourceRange.baseArrayLayer = 0;
    prePresentBarrier.subresourceRange.layerCount = 1;
    prePresentBarrier.image = info.buffers[info.current_buffer].image;
    VkImageMemoryBarrier *pmemory_barrier = &prePresentBarrier;
    vkCmdPipelineBarrier(info.cmd, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
                         VK_FALSE, 1, (const void * const*)&pmemory_barrier);

    res = vkEndCommandBuffer(info.cmd);
    const VkCommandBuffer cmd_bufs[] = { info.cmd };
    VkFence nullFence = { VK_NULL_HANDLE };

    VkSubmitInfo submit_info[1] = {};
    submit_info[0].waitSemaphoreCount = 1;
    submit_info[0].pWaitSemaphores = &presentCompleteSemaphore;
    submit_info[0].commandBufferCount = 1;
    submit_info[0].pCommandBuffers = cmd_bufs;
    submit_info[0].signalSemaphoreCount = 0;
    submit_info[0].pSignalSemaphores = NULL;

    /* Queue the command buffer for execution */
    res = vkQueueSubmit(info.queue, 1, submit_info, nullFence);
    assert(res == VK_SUCCESS);

    /* Now present the image in the window */

    VkPresentInfoKHR present;
    present.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR;
    present.pNext = NULL;
    present.swapchainCount = 1;
    present.swapchains = &info.swap_chain;
    present.imageIndices = &info.current_buffer;

    res = info.fpQueuePresentKHR(info.queue, &present);
    assert(res == VK_SUCCESS);

    wait_seconds(1);
    /* VULKAN_KEY_END */

    vkDestroySemaphore(info.device, presentCompleteSemaphore, NULL);
    destroy_descriptor_pool(info);
    destroy_framebuffers(info);
    destroy_descriptor_and_pipeline_layouts(info);
    destroy_uniform_buffer(info);
    destroy_swap_chain(info);
    destroy_command_buffer(info);
    destroy_command_pool(info);
    destroy_window(info);
    destroy_device(info);
    destroy_instance(info);
}
<|endoftext|>


<commit_before>//===-- EdgeCode.cpp - generate LLVM instrumentation code --------*- C++ -*--=//
//It implements the class EdgeCode: which provides
//support for inserting "appropriate" instrumentation at
//designated points in the graph
//
//It also has methods to insert initialization code in
//top block of cfg
//===----------------------------------------------------------------------===//

#include "llvm/Transforms/Instrumentation/Graph.h"
#include "llvm/BasicBlock.h"
#include "llvm/Constants.h"
#include "llvm/DerivedTypes.h"
#include "llvm/iMemory.h"
#include "llvm/iTerminators.h"
#include "llvm/iOther.h"
#include "llvm/iOperators.h"
#include "llvm/iPHINode.h"
#include "llvm/Module.h"
#include "llvm/SymbolTable.h"
#include "llvm/GlobalVariable.h"
#include "llvm/Constants.h"
#include "llvm/BasicBlock.h"
#include "llvm/Function.h"
#include <string.h>
#include <stdio.h>
#include <iostream>

#define INSERT_LOAD_COUNT
#define INSERT_STORE

using std::vector;

void getTriggerCode(Module *M, BasicBlock *BB, int MethNo, Value *pathNo,
		    Value *cnt){
  static int i=-1;
  i++;
  char gstr[100];
  sprintf(gstr,"globalVar%d",i);
  std::string globalVarName=gstr;
  SymbolTable *ST = M->getSymbolTable();
  vector<const Type*> args;
  //args.push_back(PointerType::get(Type::SByteTy));
  args.push_back(Type::IntTy);
  args.push_back(Type::IntTy);
  args.push_back(Type::IntTy);
  const FunctionType *MTy =
    FunctionType::get(Type::VoidTy, args, false);

  //  Function *triggerMeth = M->getOrInsertFunction("trigger", MTy);
  Function *trigMeth = M->getOrInsertFunction("trigger", MTy);
  assert(trigMeth && "trigger method could not be inserted!");
  //if (Value *triggerMeth = ST->lookup(PointerType::get(MTy), "trigger")) {
  //Function *trigMeth = cast<Function>(triggerMeth);
  vector<Value *> trargs;

  //pred_iterator piter=BB->pred_begin();
  //std::string predName = "uu";//BB->getName();
  //Constant *bbName=ConstantArray::get(predName);//BB->getName());
  //GlobalVariable *gbl=new GlobalVariable(ArrayType::get(Type::SByteTy,
  //					predName.size()+1),
  //				 true, true, bbName, gstr);

  //M->getGlobalList().push_back(gbl);

  vector<Value *> elargs;
  elargs.push_back(ConstantUInt::get(Type::UIntTy, 0));
  elargs.push_back(ConstantUInt::get(Type::UIntTy, 0));

  // commented out bb name frm which its called
  //Instruction *getElmntInst=new GetElementPtrInst(gbl,elargs,"elmntInst");

  //trargs.push_back(ConstantArray::get(BB->getName()));

  //trargs.push_back(getElmntInst);
  //trargs.push_back(bbName);

  trargs.push_back(ConstantSInt::get(Type::IntTy,MethNo));

  //trargs.push_back(ConstantSInt::get(Type::IntTy,-1));//erase this
  trargs.push_back(pathNo);
  trargs.push_back(cnt);
  Instruction *callInst=new CallInst(trigMeth,trargs);

  BasicBlock::InstListType& instList=BB->getInstList();
  BasicBlock::iterator here=instList.begin();
  //here = ++instList.insert(here, getElmntInst);
  instList.insert(here,callInst);
}

//get the code to be inserted on the edge
//This is determined from cond (1-6)
void getEdgeCode::getCode(Instruction *rInst,
			  Instruction *countInst,
			  Function *M,
			  BasicBlock *BB, int numPaths, int MethNo){

  BasicBlock::InstListType& instList=BB->getInstList();
  BasicBlock::iterator here=instList.begin();

  //case: r=k code to be inserted
  switch(cond){
  case 1:{
    Value *val=ConstantSInt::get(Type::IntTy,inc);
#ifdef INSERT_STORE
    Instruction *stInst=new StoreInst(val, rInst);
    here = ++instList.insert(here,stInst);
#endif
    break;
    }

  //case: r=0 to be inserted
  case 2:{
    Value *val=ConstantSInt::get(Type::IntTy,0);
#ifdef INSERT_STORE
    Instruction *stInst=new StoreInst(val, rInst);
    here = ++instList.insert(here,stInst);
#endif
    break;
  }

  //r+=k
  case 3:{

    Instruction *ldInst=new LoadInst(rInst, "ti1");
    Value *val=ConstantSInt::get(Type::IntTy,inc);
    Instruction *addIn=BinaryOperator::
      create(Instruction::Add, ldInst, val,"ti2");
#ifdef INSERT_STORE
    Instruction *stInst=new StoreInst(addIn, rInst);
#endif
    here = ++instList.insert(here,ldInst);
    here = ++instList.insert(here,addIn);
#ifdef INSERT_STORE
    here = ++instList.insert(here,stInst);
#endif
    break;
  }

  //count[inc]++
  case 4:{

    assert(inc>=0 && inc<=numPaths && "inc out of bound!");

    Instruction *ldInst=new
      LoadInst(countInst,vector<Value *>
	       (1,ConstantUInt::get(Type::UIntTy, inc)), "ti1");
    Value *val=ConstantSInt::get(Type::IntTy,1);
    Instruction *addIn=BinaryOperator::
      create(Instruction::Add, ldInst, val,"ti2");

    //insert trigger
    getTriggerCode(M->getParent(), BB, MethNo,
		   ConstantSInt::get(Type::IntTy,inc), addIn);
    here=instList.begin();
    //end trigger code

    assert(inc>=0 && "IT MUST BE POSITIVE NOW");
#ifdef INSERT_STORE
    Instruction *stInst=new
      StoreInst(addIn, countInst, vector<Value *>
		(1, ConstantUInt::get(Type::UIntTy,inc)));
#endif
    here = ++instList.insert(here,ldInst);
    here = ++instList.insert(here,addIn);
#ifdef INSERT_STORE
    here = ++instList.insert(here,stInst);
#endif
    break;
  }

  //case: count[r+inc]++
  case 5:{

    //ti1=inc+r
    Instruction *ldIndex=new LoadInst(rInst, "ti1");
    Value *val=ConstantSInt::get(Type::IntTy,inc);
    Instruction *addIndex=BinaryOperator::
      create(Instruction::Add, ldIndex, val,"ti2");
    //erase following 1 line
    //Value *valtemp=ConstantSInt::get(Type::IntTy,999);
    //now load count[addIndex]

    Instruction *castInst=new CastInst(addIndex,
				       Type::UIntTy,"ctin");
    Instruction *ldInst=new
      LoadInst(countInst, vector<Value *>(1,castInst), "ti3");
    Value *cons=ConstantSInt::get(Type::IntTy,1);
    //count[addIndex]++
    Instruction *addIn=BinaryOperator::
      create(Instruction::Add, ldInst, cons,"ti4");

    //insert trigger
    getTriggerCode(M->getParent(), BB, MethNo, addIndex, addIn);
    here=instList.begin();
    //end trigger code

#ifdef INSERT_STORE
    ///*
    Instruction *stInst=new
      StoreInst(addIn, countInst,
		vector<Value *>(1,castInst));
    //*/
#endif
    here = ++instList.insert(here,ldIndex);
    here = ++instList.insert(here,addIndex);
    here = ++instList.insert(here,castInst);
    here = ++instList.insert(here,ldInst);
    here = ++instList.insert(here,addIn);
#ifdef INSERT_STORE
    here = ++instList.insert(here,stInst);
#endif
    break;
  }

    //case: count[r]+
  case 6:{

    //ti1=inc+r
    Instruction *ldIndex=new LoadInst(rInst, "ti1");

    //now load count[addIndex]
    Instruction *castInst2=new
      CastInst(ldIndex, Type::UIntTy,"ctin");
    Instruction *ldInst=new
      LoadInst(countInst, vector<Value *>(1,castInst2), "ti2");
    Value *cons=ConstantSInt::get(Type::IntTy,1);

    //count[addIndex]++
    Instruction *addIn=BinaryOperator::
      create(Instruction::Add, ldInst, cons,"ti3");

    //insert trigger
    getTriggerCode(M->getParent(), BB, MethNo, ldIndex, addIn);
    here=instList.begin();
    //end trigger code
#ifdef INSERT_STORE
    Instruction *stInst=new
      StoreInst(addIn, countInst, vector<Value *>(1,castInst2));
#endif
    here = ++instList.insert(here,ldIndex);
    here = ++instList.insert(here,castInst2);
    here = instList.insert(here,ldInst);
    here = instList.insert(here,addIn);
#ifdef INSERT_STORE
    here = instList.insert(here,stInst);
#endif
    break;
  }

  }
  //now check for cdIn and cdOut
  //first put cdOut
  if(cdIn!=NULL){
    cdIn->getCode(rInst, countInst, M, BB, numPaths, MethNo);
  }
  if(cdOut!=NULL){
    cdOut->getCode(rInst, countInst, M, BB, numPaths, MethNo);
  }
}

//Insert the initialization code in the top BB
//this includes initializing r, and count
//r is like an accumulator, that
//keeps on adding increments as we traverse along a path
//and at the end of the path, r contains the path
//number of that path
//Count is an array, where Count[k] represents
//the number of executions of path k
void insertInTopBB(BasicBlock *front,
		   int k,
		   Instruction *rVar,
		   Instruction *countVar){
  //rVar is variable r,
  //countVar is array Count, and these are allocatted outside

  //store uint 0, uint *%R, uint 0
  vector<Value *> idx;
  idx.push_back(ConstantUInt::get(Type::UIntTy, 0));
  Instruction *stInstr=new StoreInst(ConstantInt::get(Type::IntTy, 0), rVar,
				     idx);

  //now push all instructions in front of the BB
  BasicBlock::InstListType& instList=front->getInstList();
  BasicBlock::iterator here=instList.begin();
  here=++front->getInstList().insert(here, rVar);
  here=++front->getInstList().insert(here,countVar);

  //Initialize Count[...] with 0

  for(int i=0;i<k; i++){
    Instruction *stInstrC=new
      StoreInst(ConstantInt::get(Type::IntTy, 0),
		countVar, std::vector<Value *>
		(1,ConstantUInt::get(Type::UIntTy, i)));
    here=++front->getInstList().insert(here,stInstrC);
  }

  here = ++front->getInstList().insert(here,stInstr);
}

//insert a basic block with appropriate code
//along a given edge
void insertBB(Edge ed,
	      getEdgeCode *edgeCode,
	      Instruction *rInst,
	      Instruction *countInst,
	      int numPaths, int Methno){
  static int i=-1;
  i++;
  BasicBlock* BB1=ed.getFirst()->getElement();
  BasicBlock* BB2=ed.getSecond()->getElement();

#ifdef DEBUG_PATH_PROFILES
  //debugging info
  cerr<<"Edges with codes ######################\n";
  cerr<<BB1->getName()<<"->"<<BB2->getName()<<"\n";
  cerr<<"########################\n";
#endif

  char counterstr[100];
  sprintf(counterstr,"counter%d",i);
  std::string ctr=counterstr;

  //We need to insert a BB between BB1 and BB2
  TerminatorInst *TI=BB1->getTerminator();
  BasicBlock *newBB=new BasicBlock(ctr, BB1->getParent());

  //get code for the new BB
  edgeCode->getCode(rInst, countInst, BB1->getParent(), newBB, numPaths, Methno);

  //Is terminator a branch instruction?
  //then we need to change branch destinations to include new BB

  //std::cerr<<"before cast!\n";
  //std::cerr<<"Method no in Edgecode:"<<Methno<<"\n";
  //std::cerr<<"Instruction\n";
  //std::cerr<<*TI;
  BranchInst *BI =  cast<BranchInst>(TI);

  if(BI->isUnconditional()){
    BI->setUnconditionalDest(newBB);
    Instruction *newBI2=new BranchInst(BB2);
    newBB->getInstList().push_back(newBI2);
  }
  else{
      if(BI->getSuccessor(0)==BB2)
      BI->setSuccessor(0, newBB);

    if(BI->getSuccessor(1)==BB2)
      BI->setSuccessor(1, newBB);

    Instruction *newBI2=new BranchInst(BB2);
    newBB->getInstList().push_back(newBI2);
  }

  //std::cerr<<"After casting\n";
  //get code for the new BB
   //now iterate over BB2, and set its Phi nodes right
  for(BasicBlock::iterator BB2Inst = BB2->begin(), BBend = BB2->end();
      BB2Inst != BBend; ++BB2Inst){

    if(PHINode *phiInst=dyn_cast<PHINode>(&*BB2Inst)){
      int bbIndex=phiInst->getBasicBlockIndex(BB1);
      assert(bbIndex>=0);
      phiInst->setIncomingBlock(bbIndex, newBB);
    }
  }
}

<commit_msg>Do not generate loads/stores with indexing<commit_after>//===-- EdgeCode.cpp - generate LLVM instrumentation code --------*- C++ -*--=//
//It implements the class EdgeCode: which provides
//support for inserting "appropriate" instrumentation at
//designated points in the graph
//
//It also has methods to insert initialization code in
//top block of cfg
//===----------------------------------------------------------------------===//

#include "llvm/Transforms/Instrumentation/Graph.h"
#include "llvm/BasicBlock.h"
#include "llvm/Constants.h"
#include "llvm/DerivedTypes.h"
#include "llvm/iMemory.h"
#include "llvm/iTerminators.h"
#include "llvm/iOther.h"
#include "llvm/iOperators.h"
#include "llvm/iPHINode.h"
#include "llvm/Module.h"
#include "llvm/SymbolTable.h"
#include "llvm/GlobalVariable.h"
#include "llvm/Constants.h"
#include "llvm/BasicBlock.h"
#include "llvm/Function.h"
#include <string.h>
#include <stdio.h>
#include <iostream>

#define INSERT_LOAD_COUNT
#define INSERT_STORE

using std::vector;

void getTriggerCode(Module *M, BasicBlock *BB, int MethNo, Value *pathNo,
		    Value *cnt){
  static int i=-1;
  i++;
  char gstr[100];
  sprintf(gstr,"globalVar%d",i);
  std::string globalVarName=gstr;
  SymbolTable *ST = M->getSymbolTable();
  vector<const Type*> args;
  //args.push_back(PointerType::get(Type::SByteTy));
  args.push_back(Type::IntTy);
  args.push_back(Type::IntTy);
  args.push_back(Type::IntTy);
  const FunctionType *MTy =
    FunctionType::get(Type::VoidTy, args, false);

  //  Function *triggerMeth = M->getOrInsertFunction("trigger", MTy);
  Function *trigMeth = M->getOrInsertFunction("trigger", MTy);
  assert(trigMeth && "trigger method could not be inserted!");
  //if (Value *triggerMeth = ST->lookup(PointerType::get(MTy), "trigger")) {
  //Function *trigMeth = cast<Function>(triggerMeth);
  vector<Value *> trargs;

  //pred_iterator piter=BB->pred_begin();
  //std::string predName = "uu";//BB->getName();
  //Constant *bbName=ConstantArray::get(predName);//BB->getName());
  //GlobalVariable *gbl=new GlobalVariable(ArrayType::get(Type::SByteTy,
  //					predName.size()+1),
  //				 true, true, bbName, gstr);

  //M->getGlobalList().push_back(gbl);

  vector<Value *> elargs;
  elargs.push_back(ConstantUInt::get(Type::UIntTy, 0));
  elargs.push_back(ConstantUInt::get(Type::UIntTy, 0));

  // commented out bb name frm which its called
  //Instruction *getElmntInst=new GetElementPtrInst(gbl,elargs,"elmntInst");

  //trargs.push_back(ConstantArray::get(BB->getName()));

  //trargs.push_back(getElmntInst);
  //trargs.push_back(bbName);

  trargs.push_back(ConstantSInt::get(Type::IntTy,MethNo));

  //trargs.push_back(ConstantSInt::get(Type::IntTy,-1));//erase this
  trargs.push_back(pathNo);
  trargs.push_back(cnt);
  Instruction *callInst=new CallInst(trigMeth,trargs);

  BasicBlock::InstListType& instList=BB->getInstList();
  BasicBlock::iterator here=instList.begin();
  //here = ++instList.insert(here, getElmntInst);
  instList.insert(here,callInst);
}

//get the code to be inserted on the edge
//This is determined from cond (1-6)
void getEdgeCode::getCode(Instruction *rInst,
			  Instruction *countInst,
			  Function *M,
			  BasicBlock *BB, int numPaths, int MethNo){

  BasicBlock::InstListType& instList=BB->getInstList();
  BasicBlock::iterator here=instList.begin();

  //case: r=k code to be inserted
  switch(cond){
  case 1:{
    Value *val=ConstantSInt::get(Type::IntTy,inc);
#ifdef INSERT_STORE
    Instruction *stInst=new StoreInst(val, rInst);
    here = ++instList.insert(here,stInst);
#endif
    break;
    }

  //case: r=0 to be inserted
  case 2:{
    Value *val=ConstantSInt::get(Type::IntTy,0);
#ifdef INSERT_STORE
    Instruction *stInst=new StoreInst(val, rInst);
    here = ++instList.insert(here,stInst);
#endif
    break;
  }

  //r+=k
  case 3:{

    Instruction *ldInst=new LoadInst(rInst, "ti1");
    Value *val=ConstantSInt::get(Type::IntTy,inc);
    Instruction *addIn=BinaryOperator::
      create(Instruction::Add, ldInst, val,"ti2");
#ifdef INSERT_STORE
    Instruction *stInst=new StoreInst(addIn, rInst);
#endif
    here = ++instList.insert(here,ldInst);
    here = ++instList.insert(here,addIn);
#ifdef INSERT_STORE
    here = ++instList.insert(here,stInst);
#endif
    break;
  }

  //count[inc]++
  case 4:{

    assert(inc>=0 && inc<=numPaths && "inc out of bound!");

    Instruction *Idx = new GetElementPtrInst(countInst,
                 vector<Value*>(1,ConstantUInt::get(Type::UIntTy, inc)));

    Instruction *ldInst=new LoadInst(Idx, "ti1");

    Value *val = ConstantSInt::get(Type::IntTy, 1);
    Instruction *addIn =
      BinaryOperator::create(Instruction::Add, ldInst, val,"ti2");

    //insert trigger
    getTriggerCode(M->getParent(), BB, MethNo,
		   ConstantSInt::get(Type::IntTy,inc), addIn);
    here=instList.begin();
    //end trigger code

    assert(inc>=0 && "IT MUST BE POSITIVE NOW");
#ifdef INSERT_STORE
    Instruction *stInst=new StoreInst(addIn, Idx);
#endif
    here = ++instList.insert(here,Idx);
    here = ++instList.insert(here,ldInst);
    here = ++instList.insert(here,addIn);
#ifdef INSERT_STORE
    here = ++instList.insert(here,stInst);
#endif
    break;
  }

  //case: count[r+inc]++
  case 5:{

    //ti1=inc+r
    Instruction *ldIndex=new LoadInst(rInst, "ti1");
    Value *val=ConstantSInt::get(Type::IntTy,inc);
    Instruction *addIndex=BinaryOperator::
      create(Instruction::Add, ldIndex, val,"ti2");
    //erase following 1 line
    //Value *valtemp=ConstantSInt::get(Type::IntTy,999);
    //now load count[addIndex]

    Instruction *castInst=new CastInst(addIndex,
				       Type::UIntTy,"ctin");
    Instruction *Idx = new GetElementPtrInst(countInst,
                                             vector<Value*>(1,castInst));

    Instruction *ldInst=new LoadInst(Idx, "ti3");
    Value *cons=ConstantSInt::get(Type::IntTy,1);
    //count[addIndex]++
    Instruction *addIn=BinaryOperator::
      create(Instruction::Add, ldInst, cons,"ti4");

    //insert trigger
    getTriggerCode(M->getParent(), BB, MethNo, addIndex, addIn);
    here=instList.begin();
    //end trigger code

#ifdef INSERT_STORE
    ///*
    Instruction *stInst=new StoreInst(addIn, Idx);

    //*/
#endif
    here = ++instList.insert(here,ldIndex);
    here = ++instList.insert(here,addIndex);
    here = ++instList.insert(here,castInst);
    here = ++instList.insert(here,Idx);
    here = ++instList.insert(here,ldInst);
    here = ++instList.insert(here,addIn);
#ifdef INSERT_STORE
    here = ++instList.insert(here,stInst);
#endif
    break;
  }

    //case: count[r]+
  case 6:{

    //ti1=inc+r
    Instruction *ldIndex=new LoadInst(rInst, "ti1");

    //now load count[addIndex]
    Instruction *castInst2=new CastInst(ldIndex, Type::UIntTy,"ctin");
    Instruction *Idx = new GetElementPtrInst(countInst,
                                             vector<Value*>(1,castInst2));

    Instruction *ldInst=new LoadInst(Idx, "ti2");
    Value *cons=ConstantSInt::get(Type::IntTy,1);

    //count[addIndex]++
    Instruction *addIn=BinaryOperator::create(Instruction::Add, ldInst,
                                              cons,"ti3");

    //insert trigger
    getTriggerCode(M->getParent(), BB, MethNo, ldIndex, addIn);
    here=instList.begin();
    //end trigger code
#ifdef INSERT_STORE
    Instruction *stInst=new StoreInst(addIn, Idx);
#endif
    here = ++instList.insert(here,ldIndex);
    here = ++instList.insert(here,castInst2);
    here = ++instList.insert(here,Idx);
    here = instList.insert(here,ldInst);
    here = instList.insert(here,addIn);
#ifdef INSERT_STORE
    here = instList.insert(here,stInst);
#endif
    break;
  }

  }
  //now check for cdIn and cdOut
  //first put cdOut
  if(cdIn!=NULL){
    cdIn->getCode(rInst, countInst, M, BB, numPaths, MethNo);
  }
  if(cdOut!=NULL){
    cdOut->getCode(rInst, countInst, M, BB, numPaths, MethNo);
  }
}

//Insert the initialization code in the top BB
//this includes initializing r, and count
//r is like an accumulator, that
//keeps on adding increments as we traverse along a path
//and at the end of the path, r contains the path
//number of that path
//Count is an array, where Count[k] represents
//the number of executions of path k
void insertInTopBB(BasicBlock *front,
		   int k,
		   Instruction *rVar,
		   Instruction *countVar){
  //rVar is variable r,
  //countVar is array Count, and these are allocatted outside

  Value *Int0 = ConstantInt::get(Type::IntTy, 0);

  //store uint 0, uint *%R, uint 0
  vector<Value *> idx;
  idx.push_back(ConstantUInt::get(Type::UIntTy, 0));

  Instruction *GEP = new GetElementPtrInst(rVar, idx);
  Instruction *stInstr=new StoreInst(Int0, GEP);

  //now push all instructions in front of the BB
  BasicBlock::InstListType& instList=front->getInstList();
  BasicBlock::iterator here=instList.begin();
  here=++front->getInstList().insert(here, rVar);
  here=++front->getInstList().insert(here,countVar);

  //Initialize Count[...] with 0

  for(int i=0;i<k; i++){
    Instruction *GEP2 = new GetElementPtrInst(countVar,
                    std::vector<Value *>(1,ConstantUInt::get(Type::UIntTy, i)));
    here=++front->getInstList().insert(here,GEP2);

    Instruction *stInstrC=new StoreInst(Int0, GEP2);
    here=++front->getInstList().insert(here,stInstrC);
  }

  here = ++front->getInstList().insert(here,GEP);
  here = ++front->getInstList().insert(here,stInstr);
}

//insert a basic block with appropriate code
//along a given edge
void insertBB(Edge ed,
	      getEdgeCode *edgeCode,
	      Instruction *rInst,
	      Instruction *countInst,
	      int numPaths, int Methno){
  static int i=-1;
  i++;
  BasicBlock* BB1=ed.getFirst()->getElement();
  BasicBlock* BB2=ed.getSecond()->getElement();

#ifdef DEBUG_PATH_PROFILES
  //debugging info
  cerr<<"Edges with codes ######################\n";
  cerr<<BB1->getName()<<"->"<<BB2->getName()<<"\n";
  cerr<<"########################\n";
#endif

  char counterstr[100];
  sprintf(counterstr,"counter%d",i);
  std::string ctr=counterstr;

  //We need to insert a BB between BB1 and BB2
  TerminatorInst *TI=BB1->getTerminator();
  BasicBlock *newBB=new BasicBlock(ctr, BB1->getParent());

  //get code for the new BB
  edgeCode->getCode(rInst, countInst, BB1->getParent(), newBB, numPaths, Methno);

  //Is terminator a branch instruction?
  //then we need to change branch destinations to include new BB

  //std::cerr<<"before cast!\n";
  //std::cerr<<"Method no in Edgecode:"<<Methno<<"\n";
  //std::cerr<<"Instruction\n";
  //std::cerr<<*TI;
  BranchInst *BI =  cast<BranchInst>(TI);

  if(BI->isUnconditional()){
    BI->setUnconditionalDest(newBB);
    Instruction *newBI2=new BranchInst(BB2);
    newBB->getInstList().push_back(newBI2);
  }
  else{
      if(BI->getSuccessor(0)==BB2)
      BI->setSuccessor(0, newBB);

    if(BI->getSuccessor(1)==BB2)
      BI->setSuccessor(1, newBB);

    Instruction *newBI2=new BranchInst(BB2);
    newBB->getInstList().push_back(newBI2);
  }

  //std::cerr<<"After casting\n";
  //get code for the new BB
   //now iterate over BB2, and set its Phi nodes right
  for(BasicBlock::iterator BB2Inst = BB2->begin(), BBend = BB2->end();
      BB2Inst != BBend; ++BB2Inst){

    if(PHINode *phiInst=dyn_cast<PHINode>(&*BB2Inst)){
      int bbIndex=phiInst->getBasicBlockIndex(BB1);
      assert(bbIndex>=0);
      phiInst->setIncomingBlock(bbIndex, newBB);
    }
  }
}

<|endoftext|>


<commit_before>// Copyright (c) 2016 ASMlover. 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 ofconditions 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 materialsprovided with the
//    distribution.
//
// 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 HOLDER 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 <stdlib.h>
#include "../basic/TConfig.h"
#include "TPoller.h"
#include "TSelectPoller.h"
#include "TPollPoller.h"

namespace tyr { namespace net {

Poller* Poller::new_default_poller(EventLoop* loop) {
  if (getenv("TYR_USE_SELECT")) {
    return new SelectPoller(loop);
  }
  else if (getenv("TYR_USE_POLL")) {
    return new PollPoller(loop);
  }

  return nullptr;
}

}}
<commit_msg>:construction: chore(poller): updated the poller generator function<commit_after>// Copyright (c) 2016 ASMlover. 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 ofconditions 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 materialsprovided with the
//    distribution.
//
// 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 HOLDER 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 <stdlib.h>
#include "../basic/TConfig.h"
#include "TPoller.h"
#include "TSelectPoller.h"
#include "TPollPoller.h"
#include "TAsyncPoller.h"

namespace tyr { namespace net {

Poller* Poller::new_default_poller(EventLoop* loop) {
  if (getenv("TYR_USE_SELECT"))
    return new SelectPoller(loop);
  else if (getenv("TYR_USE_POLL"))
    return new PollPoller(loop);
  else
    return new AsyncPoller(loop);
}

}}
<|endoftext|>


<commit_before>/** \file    add_additional_relbib_entries.cc
 *  \author  Johannes Riedl
 *
 *  A tool for tagging entries that are not yet officially part of
 *  the set of relbib titles but were indentified to be relevant
 */

/*
    Copyright (C) 2016-2018, Library of the University of Tübingen

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

    This program 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 Affero General Public License for more details.

    You should have received a copy of the GNU Affero General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#include <iostream>
#include <unordered_map>
#include <cstdlib>
#include "Compiler.h"
#include "FileUtil.h"
#include "MARC.h"
#include "util.h"

static unsigned modified_count(0);
static unsigned record_count(0);

const std::string RELBIB_RELEVANT_IDS_FILENAME("/usr/local/ub_tools/cpp/data/relbib_auto_list.txt");
const std::string RELBIB_RELEVANT_TAG("191");
const char RELBIB_SUBFIELD('a');

namespace {

[[noreturn]] void Usage() {
    std::cerr << "Usage: " << ::progname << " marc_input marc_output\n"
              << "       Tags entries that are not yet officially part of the set of titles relevant for relbib\n"
              << "       but have been identified to be probably relevant.\n";
    std::exit(EXIT_FAILURE);
}

void ProcessRecord(MARC::Record * const record, const std::unordered_set<std::string> &relbib_relevant_set) {
     if (relbib_relevant_set.find(record->getControlNumber()) != relbib_relevant_set.end()) {
         if (record->findTag(RELBIB_RELEVANT_TAG) != record->end())
             LOG_ERROR("Field " + RELBIB_RELEVANT_TAG + " already populated for PPN " + record->getControlNumber());
         record->addSubfield(RELBIB_RELEVANT_TAG, RELBIB_SUBFIELD, "1");
         ++modified_count;
     }
}

void TagRelevantRecords(MARC::Reader * const marc_reader, MARC::Writer * const marc_writer,
                        const std::unordered_set<std::string> &relbib_relevant_set)
{
    unsigned total_count(0);
    while (MARC::Record record = marc_reader->read()) {
        ProcessRecord(&record, relbib_relevant_set);
        marc_writer->write(record);
        ++record_count;
    }

    std::cout << "Processed a total of " << total_count << " record(s).\n";
    std::cout << "Modified " << modified_count << " of " << record_count << " record(s).\n";
}

void SetupRelBibRelevantSet(std::unordered_set<std::string> * const relbib_relevant_set) {
    std::unique_ptr<File> relbib_relevant(FileUtil::OpenInputFileOrDie(RELBIB_RELEVANT_IDS_FILENAME));
    std::string line;
    int retval;
    while ((retval = relbib_relevant->getline(&line, '\n'))) {
        if (not retval) {
            if (unlikely(relbib_relevant->anErrorOccurred()))
                LOG_ERROR("Error on reading in relbib relevant file " + relbib_relevant->getPath());
            if (unlikely(relbib_relevant->eof()))
                return;
            continue;
        }
        relbib_relevant_set->emplace(line);
    }
}

} // unnamed namespace

int Main(int argc, char **argv) {
    if (argc != 3)
        Usage();

    const std::string marc_input_filename(argv[1]);
    const std::string marc_output_filename(argv[2]);
    if (unlikely(marc_input_filename == marc_output_filename))
        LOG_ERROR("Title data input file name equals output file name!");

    auto marc_reader(MARC::Reader::Factory(marc_input_filename));
    auto marc_writer(MARC::Writer::Factory(marc_output_filename));

    std::unordered_set<std::string> relbib_relevant_set;
    SetupRelBibRelevantSet(&relbib_relevant_set);
    TagRelevantRecords(marc_reader.get(), marc_writer.get(), relbib_relevant_set);

    return EXIT_SUCCESS;
}
<commit_msg>Fixed error that prevented record from being modified<commit_after>/** \file    add_additional_relbib_entries.cc
 *  \author  Johannes Riedl
 *
 *  A tool for tagging entries that are not yet officially part of
 *  the set of relbib titles but were indentified to be relevant
 */

/*
    Copyright (C) 2016-2018, Library of the University of Tübingen

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

    This program 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 Affero General Public License for more details.

    You should have received a copy of the GNU Affero General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#include <iostream>
#include <unordered_map>
#include <cstdlib>
#include "Compiler.h"
#include "FileUtil.h"
#include "MARC.h"
#include "util.h"

static unsigned modified_count(0);
static unsigned record_count(0);

const std::string RELBIB_RELEVANT_IDS_FILENAME("/usr/local/ub_tools/cpp/data/relbib_auto_list.txt");
const std::string RELBIB_RELEVANT_TAG("191");
const char RELBIB_SUBFIELD('a');

namespace {

[[noreturn]] void Usage() {
    std::cerr << "Usage: " << ::progname << " marc_input marc_output\n"
              << "       Tags entries that are not yet officially part of the set of titles relevant for relbib\n"
              << "       but have been identified to be probably relevant.\n";
    std::exit(EXIT_FAILURE);
}

void ProcessRecord(MARC::Record * const record, const std::unordered_set<std::string> &relbib_relevant_set) {
     if (relbib_relevant_set.find(record->getControlNumber()) != relbib_relevant_set.end()) {
         if (record->findTag(RELBIB_RELEVANT_TAG) != record->end())
             LOG_ERROR("Field " + RELBIB_RELEVANT_TAG + " already populated for PPN " + record->getControlNumber());
         record->insertField(RELBIB_RELEVANT_TAG, { { RELBIB_SUBFIELD, "1" } });
         ++modified_count;
     }
}

void TagRelevantRecords(MARC::Reader * const marc_reader, MARC::Writer * const marc_writer,
                        const std::unordered_set<std::string> &relbib_relevant_set)
{
    while (MARC::Record record = marc_reader->read()) {
        ProcessRecord(&record, relbib_relevant_set);
        marc_writer->write(record);
        ++record_count;
    }

    std::cout << "Modified " << modified_count << " of " << record_count << " record(s).\n";
}

void SetupRelBibRelevantSet(std::unordered_set<std::string> * const relbib_relevant_set) {
    std::unique_ptr<File> relbib_relevant(FileUtil::OpenInputFileOrDie(RELBIB_RELEVANT_IDS_FILENAME));
    std::string line;
    int retval;
    while ((retval = relbib_relevant->getline(&line, '\n'))) {
        if (not retval) {
            if (unlikely(relbib_relevant->anErrorOccurred()))
                LOG_ERROR("Error on reading in relbib relevant file " + relbib_relevant->getPath());
            if (unlikely(relbib_relevant->eof()))
                return;
            continue;
        }
        relbib_relevant_set->emplace(line);
    }
}

} // unnamed namespace

int Main(int argc, char **argv) {
    if (argc != 3)
        Usage();

    const std::string marc_input_filename(argv[1]);
    const std::string marc_output_filename(argv[2]);
    if (unlikely(marc_input_filename == marc_output_filename))
        LOG_ERROR("Title data input file name equals output file name!");

    auto marc_reader(MARC::Reader::Factory(marc_input_filename));
    auto marc_writer(MARC::Writer::Factory(marc_output_filename));

    std::unordered_set<std::string> relbib_relevant_set;
    SetupRelBibRelevantSet(&relbib_relevant_set);
    TagRelevantRecords(marc_reader.get(), marc_writer.get(), relbib_relevant_set);

    return EXIT_SUCCESS;
}
<|endoftext|>


<commit_before>/** \file   generate_subscription_packets.cc
 *  \brief  Imports data from Zeder and writes a subscription packets defintion file.
 *  \author Dr. Johannes Ruscheinski (johannes.ruscheinski@uni-tuebingen.de)
 *  \copyright 2020 Universitätsbibliothek Tübingen.  All rights reserved.
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU Affero General Public License as
 *  published by the Free Software Foundation, either version 3 of the
 *  License, or (at your option) any later version.
 *
 *  This program 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 Affero General Public License for more details.
 *
 *  You should have received a copy of the GNU Affero General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <map>
#include <unordered_map>
#include "Compiler.h"
#include "Downloader.h"
#include "FileUtil.h"
#include "IniFile.h"
#include "JSON.h"
#include "StringUtil.h"
#include "util.h"

namespace {

const std::string ZEDER_URL_PREFIX(
    "http://www-ub.ub.uni-tuebingen.de/zeder/cgi-bin/zeder.cgi?action=get&Dimension=wert&Bearbeiter=&Instanz=");

void GetZederJSON(const std::string &zeder_instance, std::string * const json_blob) {
    Downloader downloader(ZEDER_URL_PREFIX + zeder_instance);
    if (downloader.anErrorOccurred())
        LOG_ERROR("failed to download Zeder data: " + downloader.getLastErrorMessage());

    const auto http_response_code(downloader.getResponseCode());
    if (http_response_code < 200 or http_response_code > 399)
        LOG_ERROR("got bad HTTP response code: " + std::to_string(http_response_code));

    *json_blob = downloader.getMessageBody();
}

typedef std::unordered_map<std::string, std::map<std::string, std::string>> ColumnNamesToEnumMaps;

// Returns a mapping from short column name to a map from short integer values as strings to symbolic enum names.
// This is needed because columns containing enum values are not specified with the actual enum constants but with
// small integers which must be mapped to the actual enum constants.
ColumnNamesToEnumMaps GetZederEnumMappings(const JSON::ArrayNode &meta_array) {
    ColumnNamesToEnumMaps column_names_to_enum_maps;
    for (const auto &entry : meta_array) {
        const auto enum_object(JSON::JSONNode::CastToObjectNodeOrDie("enum_object", entry));
        const auto short_column_name(enum_object->getStringNode("Kurz")->getValue());
        const auto enum_values_array(enum_object->getArrayNode("Optionen"));
        if (enum_values_array->empty())
            continue;

        std::map<std::string, std::string> enum_map;
        for (const auto &enum_value_desc : *enum_values_array) {
            const auto enum_value_object(JSON::JSONNode::CastToObjectNodeOrDie("enum_value_object", enum_value_desc));
            enum_map.emplace(enum_value_object->getIntegerNode("id")->toString(),
                             enum_value_object->getStringNode("wert")->getValue());

        }
        column_names_to_enum_maps.emplace(short_column_name, enum_map);
    }

    LOG_INFO("found " + std::to_string(column_names_to_enum_maps.size()) + " enum maps.");
    return column_names_to_enum_maps;
}

// In order to understand this function you need the following information:
// 1. Someone, who will remain unnamed to protect the guilty has, in his infinite wisdom (that was sacrasm for the Americans..)
//    decided not to simply have empty values but to instead use NV (= no value??)
// 2. There appear to be two kinds of legitimate values, real immediate value and values that represent an enum value.
//    The enum values are not provided as literals but are represented by small integers instead.  In order to convert those
//    integers to their string literals equivalents we need to use "column_names_to_enum_maps."  Fo those columns for which
//    such a mapping exists we have the complete mapping stored under the short column name key in "column_names_to_enum_maps."
//    IOW, if the short column name does not exist in "column_names_to_enum_maps" it is not a enum-valued column and the value
//    should be taken as is and not mapped.
std::string GetString(const JSON::ObjectNode &journal_node, const std::string &key, const ColumnNamesToEnumMaps &column_names_to_enum_maps) {
    if (not journal_node.hasNode(key))
        return "";

    const auto value(journal_node.getStringNode(key)->getValue());
    if (value == "NV")
        return "";

    const auto short_column_name_and_enum_map(column_names_to_enum_maps.find(key));
    if (short_column_name_and_enum_map == column_names_to_enum_maps.cend())
        return value;

    const auto integer_and_symbolic_enum_value(short_column_name_and_enum_map->second.find(value));
    if (unlikely(integer_and_symbolic_enum_value == short_column_name_and_enum_map->second.cend()))
        LOG_ERROR("no mapping from \"" + value + "\" to a symbolic enum value found for column \"" + key + "\"!");

    return integer_and_symbolic_enum_value->second;
}

// Return true if an entry of "class_list" equals one of the vertical-bar-separated values of "expected_values_str."
bool FoundExpectedClassValue(const std::string &expected_values_str, const std::string &class_list_str) {
    std::vector<std::string> expected_values;
    StringUtil::Split(expected_values_str, '|', &expected_values);

    std::vector<std::string> class_list;
    StringUtil::SplitThenTrimWhite(class_list_str, ',', &class_list);

    for (const auto &class_str : class_list) {
        if (std::find_if(expected_values.cbegin(), expected_values.cend(),
                         [&class_str](auto &expected_value) { return class_str == expected_value; }) != expected_values.cend())
            return true;
    }
    return false;
}

bool IncludeJournal(const JSON::ObjectNode &journal_object, const IniFile::Section &filter_section,
                    const ColumnNamesToEnumMaps &column_names_to_enum_maps)
{
    for (const auto &entry : filter_section) {
        if (entry.name_ == "description")
            continue;

        std::string zeder_column_name(entry.name_);
        if (entry.name_ == "except_class")
            zeder_column_name = "class";

        const auto node(journal_object.getNode(zeder_column_name));
        if (node == nullptr)
            continue;

        const auto value_as_string(GetString(journal_object, zeder_column_name, column_names_to_enum_maps));
        if (value_as_string.empty())
            return false;

        if (zeder_column_name != "class") {
            if (value_as_string != entry.value_)
                return false;
        } else { // class or except_class
            const bool found_it(FoundExpectedClassValue(entry.value_, value_as_string));
            if ((not found_it and entry.name_ == "class")
                or (found_it and entry.name_ == "except_class"))
                return false;
        }
    }

    return true;
}

// Please note that Zeder PPN entries are separated by spaces and, unlike what the column names "print_ppn" and
// "online_ppn" imply may in rare cases contain space-separated lists of PPN's.
void ProcessPPNs(const std::string &ppns, unsigned * const ppn_counter, File * const output_file) {
    std::vector<std::string> individual_ppns;
    StringUtil::Split(ppns, ' ', &individual_ppns);
    for (const auto &ppn : individual_ppns) {
        ++*ppn_counter;
        (*output_file) << "ppn" << (*ppn_counter) << " = " << ppn << '\n';
    }
}

void GenerateBundleDefinition(const JSON::ArrayNode &journals_array, const IniFile::Section &section,
                              const ColumnNamesToEnumMaps &column_names_to_enum_maps, File * const output_file)
{
    unsigned included_journal_count(0), ppn_counter(0);
    (*output_file) << '[' << section.getSectionName() << "]\n";
    for (const auto &entry : journals_array) {
        const auto journal_object(JSON::JSONNode::CastToObjectNodeOrDie("entry", entry));
        if (not IncludeJournal(*journal_object, section, column_names_to_enum_maps))
            continue;

        ++included_journal_count;
        const auto print_ppns(GetString(*journal_object, "pppn", column_names_to_enum_maps));
        const auto online_ppns(GetString(*journal_object, "eppn", column_names_to_enum_maps));

        if (print_ppns.empty() and online_ppns.empty()) {
            --included_journal_count;
            const auto zeder_id(std::to_string(journal_object->getIntegerNode("DT_RowId")->getValue()));
            LOG_WARNING("Zeder entry #" + zeder_id + " is missing print and online PPN's!");
            continue;
        }

        ProcessPPNs(print_ppns, &ppn_counter, output_file);
        ProcessPPNs(online_ppns, &ppn_counter, output_file);
    }

    LOG_INFO("included " + std::to_string(included_journal_count) + " journal(s) with " + std::to_string(ppn_counter)
             + " PPN's in the bundle for \"" + section.getSectionName() + "\".");
}

} // unnamed namespace

int Main(int argc, char *argv[]) {
    if (argc != 3)
        ::Usage("packet_definition_config_file packet_subscriptions_output\n"
                "\tFor the documentation of the input config file, please see data/generate_subscription_packets.README.");

    const IniFile packet_definitions_ini_file(argv[1]);
    const auto zeder_instance(packet_definitions_ini_file.getString("", "instance"));

    std::string json_blob;
    GetZederJSON(zeder_instance, &json_blob);
    FileUtil::WriteString("blob.json", json_blob);

    JSON::Parser parser(json_blob);
    std::shared_ptr<JSON::JSONNode> tree_root;
    if (not parser.parse(&tree_root))
        LOG_ERROR("failed to parse the Zeder JSON: " + parser.getErrorMessage());

    // Zeder JSON has two top-level keys, "meta" and "daten."  "daten" contains the actual rows corresponding to one journal
    // per row.  "meta" contains mappings from small integers to enumerated constants for those columns that use enum values.
    const auto root_node(JSON::JSONNode::CastToObjectNodeOrDie("tree_root", tree_root));
    if (not root_node->hasNode("meta"))
        LOG_ERROR("top level object of Zeder JSON does not have a \"meta\" key!");
    const auto column_names_to_enum_maps(GetZederEnumMappings(*JSON::JSONNode::CastToArrayNodeOrDie("meta", root_node->getNode("meta"))));
    if (not root_node->hasNode("daten"))
        LOG_ERROR("top level object of Zeder JSON does not have a \"daten\" key!");
    const auto daten(JSON::JSONNode::CastToArrayNodeOrDie("daten", root_node->getNode("daten")));

    const auto bundle_definitions_output_file(FileUtil::OpenOutputFileOrDie(argv[2]));
    for (const auto &section : packet_definitions_ini_file) {
        if (section.getSectionName().empty())
            continue; // Skip the global section.
        GenerateBundleDefinition(*daten, section, column_names_to_enum_maps, bundle_definitions_output_file.get());
    }

    return EXIT_SUCCESS;
}
<commit_msg>Be more careful in how we process column values.<commit_after>/** \file   generate_subscription_packets.cc
 *  \brief  Imports data from Zeder and writes a subscription packets defintion file.
 *  \author Dr. Johannes Ruscheinski (johannes.ruscheinski@uni-tuebingen.de)
 *  \copyright 2020 Universitätsbibliothek Tübingen.  All rights reserved.
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU Affero General Public License as
 *  published by the Free Software Foundation, either version 3 of the
 *  License, or (at your option) any later version.
 *
 *  This program 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 Affero General Public License for more details.
 *
 *  You should have received a copy of the GNU Affero General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <map>
#include <unordered_map>
#include "Compiler.h"
#include "Downloader.h"
#include "FileUtil.h"
#include "IniFile.h"
#include "JSON.h"
#include "StringUtil.h"
#include "util.h"

namespace {

const std::string ZEDER_URL_PREFIX(
    "http://www-ub.ub.uni-tuebingen.de/zeder/cgi-bin/zeder.cgi?action=get&Dimension=wert&Bearbeiter=&Instanz=");

void GetZederJSON(const std::string &zeder_instance, std::string * const json_blob) {
    Downloader downloader(ZEDER_URL_PREFIX + zeder_instance);
    if (downloader.anErrorOccurred())
        LOG_ERROR("failed to download Zeder data: " + downloader.getLastErrorMessage());

    const auto http_response_code(downloader.getResponseCode());
    if (http_response_code < 200 or http_response_code > 399)
        LOG_ERROR("got bad HTTP response code: " + std::to_string(http_response_code));

    *json_blob = downloader.getMessageBody();
}

typedef std::unordered_map<std::string, std::map<std::string, std::string>> ColumnNamesToEnumMaps;

// Returns a mapping from short column name to a map from short integer values as strings to symbolic enum names.
// This is needed because columns containing enum values are not specified with the actual enum constants but with
// small integers which must be mapped to the actual enum constants.
ColumnNamesToEnumMaps GetZederEnumMappings(const JSON::ArrayNode &meta_array) {
    ColumnNamesToEnumMaps column_names_to_enum_maps;
    for (const auto &entry : meta_array) {
        const auto enum_object(JSON::JSONNode::CastToObjectNodeOrDie("enum_object", entry));
        const auto short_column_name(enum_object->getStringNode("Kurz")->getValue());
        const auto enum_values_array(enum_object->getArrayNode("Optionen"));
        if (enum_values_array->empty())
            continue;

        std::map<std::string, std::string> enum_map;
        for (const auto &enum_value_desc : *enum_values_array) {
            const auto enum_value_object(JSON::JSONNode::CastToObjectNodeOrDie("enum_value_object", enum_value_desc));
            enum_map.emplace(enum_value_object->getIntegerNode("id")->toString(),
                             enum_value_object->getStringNode("wert")->getValue());

        }
        column_names_to_enum_maps.emplace(short_column_name, enum_map);
    }

    LOG_INFO("found " + std::to_string(column_names_to_enum_maps.size()) + " enum maps.");
    return column_names_to_enum_maps;
}

// In order to understand this function you need the following information:
// 1. Someone, who will remain unnamed to protect the guilty has, in his infinite wisdom (that was sacrasm for the Americans..)
//    decided not to simply have empty values but to instead use NV (= no value??)
// 2. There appear to be two kinds of legitimate values, real immediate value and values that represent an enum value.
//    The enum values are not provided as literals but are represented by small integers instead.  In order to convert those
//    integers to their string literals equivalents we need to use "column_names_to_enum_maps."  Fo those columns for which
//    such a mapping exists we have the complete mapping stored under the short column name key in "column_names_to_enum_maps."
//    IOW, if the short column name does not exist in "column_names_to_enum_maps" it is not a enum-valued column and the value
//    should be taken as is and not mapped.
std::string GetString(const JSON::ObjectNode &journal_node, const std::string &key, const ColumnNamesToEnumMaps &column_names_to_enum_maps) {
    if (not journal_node.hasNode(key))
        return "";

    const auto value(journal_node.getStringNode(key)->getValue());
    if (value == "NV")
        return "";

    const auto short_column_name_and_enum_map(column_names_to_enum_maps.find(key));
    if (short_column_name_and_enum_map == column_names_to_enum_maps.cend())
        return value;

    const auto integer_and_symbolic_enum_value(short_column_name_and_enum_map->second.find(value));
    if (unlikely(integer_and_symbolic_enum_value == short_column_name_and_enum_map->second.cend()))
        LOG_ERROR("no mapping from \"" + value + "\" to a symbolic enum value found for column \"" + key + "\"!");

    return integer_and_symbolic_enum_value->second;
}

// Return true if an entry of "class_list" equals one of the vertical-bar-separated values of "expected_values_str."
bool FoundExpectedClassValue(const std::string &expected_values_str, const std::string &class_list_str) {
    std::vector<std::string> expected_values;
    StringUtil::Split(expected_values_str, '|', &expected_values);

    std::vector<std::string> class_list;
    StringUtil::SplitThenTrimWhite(class_list_str, ',', &class_list);

    for (const auto &class_str : class_list) {
        if (std::find_if(expected_values.cbegin(), expected_values.cend(),
                         [&class_str](auto &expected_value) { return class_str == expected_value; }) != expected_values.cend())
            return true;
    }
    return false;
}

bool IncludeJournal(const JSON::ObjectNode &journal_object, const IniFile::Section &filter_section,
                    const ColumnNamesToEnumMaps &column_names_to_enum_maps)
{
    std::string true_string;
    for (const auto &entry : filter_section) {
        if (entry.name_.empty() or entry.name_ == "description")
            continue;

        std::string zeder_column_name(entry.name_);
        if (entry.name_ == "except_class")
            zeder_column_name = "class";

        const auto node(journal_object.getNode(zeder_column_name));
        if (node == nullptr)
            return false;

        const auto value_as_string(StringUtil::TrimWhite(GetString(journal_object, zeder_column_name, column_names_to_enum_maps)));
        if (value_as_string.empty())
            return false;
        true_string += " " + zeder_column_name + ":" + value_as_string;

        if (zeder_column_name != "class") {
            if (::strcasecmp(value_as_string.c_str(),entry.value_.c_str()) != 0)
                return false;
        } else { // class or except_class
            const bool found_it(FoundExpectedClassValue(entry.value_, value_as_string));
            if ((not found_it and entry.name_ == "class")
                or (found_it and entry.name_ == "except_class"))
                return false;
        }
    }

    return true;
}

// Please note that Zeder PPN entries are separated by spaces and, unlike what the column names "print_ppn" and
// "online_ppn" imply may in rare cases contain space-separated lists of PPN's.
void ProcessPPNs(const std::string &ppns, unsigned * const ppn_counter, File * const output_file) {
    std::vector<std::string> individual_ppns;
    StringUtil::Split(ppns, ' ', &individual_ppns);
    for (const auto &ppn : individual_ppns) {
        ++*ppn_counter;
        (*output_file) << "ppn" << (*ppn_counter) << " = " << ppn << '\n';
    }
}

void GenerateBundleDefinition(const JSON::ArrayNode &journals_array, const IniFile::Section &section,
                              const ColumnNamesToEnumMaps &column_names_to_enum_maps, File * const output_file)
{
    unsigned included_journal_count(0), ppn_counter(0);
    (*output_file) << '[' << section.getSectionName() << "]\n";
    for (const auto &entry : journals_array) {
        const auto journal_object(JSON::JSONNode::CastToObjectNodeOrDie("entry", entry));
        if (not IncludeJournal(*journal_object, section, column_names_to_enum_maps))
            continue;

        const auto ZEDER_ID(journal_object->getIntegerNode("DT_RowId")->toString());
        ++included_journal_count;
        const auto print_ppns(GetString(*journal_object, "pppn", column_names_to_enum_maps));
        const auto online_ppns(GetString(*journal_object, "eppn", column_names_to_enum_maps));
        const std::string all_ppns(print_ppns + online_ppns);
        if (all_ppns.find("1290746834") != std::string::npos)
            LOG_ERROR("ID: " + ZEDER_ID);

        if (print_ppns.empty() and online_ppns.empty()) {
            --included_journal_count;
            const auto zeder_id(std::to_string(journal_object->getIntegerNode("DT_RowId")->getValue()));
            LOG_WARNING("Zeder entry #" + zeder_id + " is missing print and online PPN's!");
            continue;
        }

        ProcessPPNs(print_ppns, &ppn_counter, output_file);
        ProcessPPNs(online_ppns, &ppn_counter, output_file);
    }

    LOG_INFO("included " + std::to_string(included_journal_count) + " journal(s) with " + std::to_string(ppn_counter)
             + " PPN's in the bundle for \"" + section.getSectionName() + "\".");
}

} // unnamed namespace

int Main(int argc, char *argv[]) {
    if (argc != 3)
        ::Usage("packet_definition_config_file packet_subscriptions_output\n"
                "\tFor the documentation of the input config file, please see data/generate_subscription_packets.README.");

    const IniFile packet_definitions_ini_file(argv[1]);
    const auto zeder_instance(packet_definitions_ini_file.getString("", "instance"));

    std::string json_blob;
    GetZederJSON(zeder_instance, &json_blob);
    FileUtil::WriteString("blob.json", json_blob);

    JSON::Parser parser(json_blob);
    std::shared_ptr<JSON::JSONNode> tree_root;
    if (not parser.parse(&tree_root))
        LOG_ERROR("failed to parse the Zeder JSON: " + parser.getErrorMessage());

    // Zeder JSON has two top-level keys, "meta" and "daten."  "daten" contains the actual rows corresponding to one journal
    // per row.  "meta" contains mappings from small integers to enumerated constants for those columns that use enum values.
    const auto root_node(JSON::JSONNode::CastToObjectNodeOrDie("tree_root", tree_root));
    if (not root_node->hasNode("meta"))
        LOG_ERROR("top level object of Zeder JSON does not have a \"meta\" key!");
    const auto column_names_to_enum_maps(GetZederEnumMappings(*JSON::JSONNode::CastToArrayNodeOrDie("meta", root_node->getNode("meta"))));
    if (not root_node->hasNode("daten"))
        LOG_ERROR("top level object of Zeder JSON does not have a \"daten\" key!");
    const auto daten(JSON::JSONNode::CastToArrayNodeOrDie("daten", root_node->getNode("daten")));

    const auto bundle_definitions_output_file(FileUtil::OpenOutputFileOrDie(argv[2]));
    for (const auto &section : packet_definitions_ini_file) {
        if (section.getSectionName().empty())
            continue; // Skip the global section.
        GenerateBundleDefinition(*daten, section, column_names_to_enum_maps, bundle_definitions_output_file.get());
    }

    return EXIT_SUCCESS;
}
<|endoftext|>


<commit_before>/** \brief Utility for augmenting MARC records with links to a local full-text database.
 *  \author Dr. Johannes Ruscheinski (johannes.ruscheinski@uni-tuebingen.de)
 *
 *  \copyright 2015-2017 Universitätsbibliothek Tübingen.  All rights reserved.
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU Affero General Public License as
 *  published by the Free Software Foundation, either version 3 of the
 *  License, or (at your option) any later version.
 *
 *  This program 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 Affero General Public License for more details.
 *
 *  You should have received a copy of the GNU Affero General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <algorithm>
#include <iostream>
#include <map>
#include <memory>
#include <stdexcept>
#include <vector>
#include <climits>
#include <cstdio>
#include <cstdlib>
#include <libgen.h>
#include <strings.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <kchashdb.h>
#include "Downloader.h"
#include "ExecUtil.h"
#include "FileUtil.h"
#include "MarcReader.h"
#include "MarcRecord.h"
#include "MarcUtil.h"
#include "MarcWriter.h"
#include "RegexMatcher.h"
#include "Semaphore.h"
#include "StringUtil.h"
#include "Subfields.h"
#include "TimeLimit.h"
#include "util.h"

static void Usage() __attribute__((noreturn));

static void Usage() {
    std::cerr << "Usage: " << ::progname
              << "[--max-record-count count] [--skip-count count] [--process-count-low-and-high-watermarks low:high] "
              << "marc_input marc_output full_text_db error_log_path\n"
              << "       --process-count-low-and-high-watermarks sets the maximum and minimum number of spawned\n"
              << "       child processes.  When we hit the high water mark we wait for child processes to exit\n"
              << "       until we reach the low watermark.\n\n";

    std::exit(EXIT_FAILURE);
}

// Checks subfields "3" and "z" to see if they start w/ "Rezension".
bool IsProbablyAReview(const Subfields &subfields) {
    const auto &_3_begin_end(subfields.getIterators('3'));
    if (_3_begin_end.first != _3_begin_end.second) {
        if (StringUtil::StartsWith(_3_begin_end.first->value_, "Rezension"))
            return true;
    } else {
        const auto &z_begin_end(subfields.getIterators('z'));
        if (z_begin_end.first != z_begin_end.second
            and StringUtil::StartsWith(z_begin_end.first->value_, "Rezension"))
            return true;
    }

    return false;
}

bool FoundAtLeastOneNonReviewLink(const MarcRecord &record) {
    for (size_t _856_index(record.getFieldIndex("856")); record.getTag(_856_index) == "856"; ++_856_index) {
        const Subfields subfields(record.getSubfields(_856_index));
        if (subfields.getIndicator1() == '7' or not subfields.hasSubfield('u'))
            continue;

        if (not IsProbablyAReview(subfields))
            return true;
    }

    return false;
}

// Returns the number of child processes that returned a non-zero exit code.
unsigned CleanUpZombies(const unsigned zombies_to_collect) {
    unsigned child_reported_failure_count(0);
    for (unsigned zombie_no(0); zombie_no < zombies_to_collect; ++zombie_no) {
        int exit_code;
        ::wait(&exit_code);
        if (exit_code != 0)
            ++child_reported_failure_count;
    }

    return child_reported_failure_count;
}

void ProcessRecords(const unsigned max_record_count, const unsigned skip_count, MarcReader * const marc_reader,
                    MarcWriter * const marc_writer, const std::string &db_filename, const std::string &error_log_path,
                    const unsigned process_count_low_watermark, const unsigned process_count_high_watermark)
{
    Semaphore semaphore("/full_text_cached_counter", Semaphore::CREATE);
    std::string err_msg;
    unsigned total_record_count(0), spawn_count(0), active_child_count(0), child_reported_failure_count(0);

    const std::string UPDATE_FULL_TEXT_DB_PATH("/usr/local/bin/update_full_text_db");

    std::cout << "Skip " << skip_count << " records\n";
    off_t record_start = marc_reader->tell();
    while (MarcRecord record = marc_reader->read()) {
        if (total_record_count == max_record_count)
            break;
        ++total_record_count;
        if (total_record_count <= skip_count) {
            record_start = marc_reader->tell();
            continue;
        }

        const bool insert_in_cache(FoundAtLeastOneNonReviewLink(record)
                                   or (not MarcUtil::HasTagAndSubfield(record, "856", 'u')
                                       and MarcUtil::HasTagAndSubfield(record, "520", 'a')));
        if (not insert_in_cache) {
            MarcUtil::FileLockedComposeAndWriteRecord(marc_writer, &record);
            record_start = marc_reader->tell();
            continue;
        }

        ExecUtil::Spawn(UPDATE_FULL_TEXT_DB_PATH,
                        { std::to_string(record_start), marc_reader->getPath(), marc_writer->getFile().getPath(),
                                db_filename, error_log_path });
        ++active_child_count;
        ++spawn_count;

        if (active_child_count > process_count_high_watermark) {
            child_reported_failure_count += CleanUpZombies(active_child_count - process_count_low_watermark);
            active_child_count = process_count_low_watermark;
        }
        record_start = marc_reader->tell();
    }

    // Wait for stragglers:
    child_reported_failure_count += CleanUpZombies(active_child_count);

    if (not err_msg.empty())
        Error(err_msg);
    std::cerr << "Read " << total_record_count << " records.\n";
    std::cerr << "Spawned " << spawn_count << " subprocesses.\n";
    std::cerr << semaphore.getValue()
              << " documents were not downloaded because their cached values had not yet expired.\n";
    std::cerr << child_reported_failure_count << " children reported a failure!\n";
}

constexpr unsigned PROCESS_COUNT_DEFAULT_HIGH_WATERMARK(10);
constexpr unsigned PROCESS_COUNT_DEFAULT_LOW_WATERMARK(5);

int main(int argc, char **argv) {
    ::progname = argv[0];

    if (argc != 5 and argc != 7 and argc != 9 and argc != 11)
        Usage();
    ++argv; // skip program name

    // Process optional args:
    unsigned max_record_count(UINT_MAX), skip_count(0);
    unsigned process_count_low_watermark(PROCESS_COUNT_DEFAULT_LOW_WATERMARK),
             process_count_high_watermark(PROCESS_COUNT_DEFAULT_HIGH_WATERMARK);
    while (argc > 5) {
        std::cout << "Arg: " << *argv << "\n";
        if (std::strcmp(*argv, "--max-record-count") == 0) {
            ++argv;
            if (not StringUtil::ToNumber(*argv, &max_record_count) or max_record_count == 0)
                Error("bad value for --max-record-count!");
            ++argv;
            argc -= 2;
        } else if (std::strcmp(*argv, "--skip-count") == 0) {
            ++argv;
            if (not StringUtil::ToNumber(*argv, &skip_count))
                Error("bad value for --skip-count!");
            std::cout << "Should skip " << skip_count << " records\n";
            ++argv;
            argc -= 2;
        } else if (std::strcmp("--process-count-low-and-high-watermarks", *argv) == 0) {
            ++argv;
            char *arg_end(*argv + std::strlen(*argv));
            char * const colon(std::find(*argv, arg_end, ':'));
            if (colon == arg_end)
                Error("bad argument to --process-count-low-and-high-watermarks: colon is missing!");
            *colon = '\0';
            if (not StringUtil::ToNumber(*argv, &process_count_low_watermark)
                or not StringUtil::ToNumber(*argv, &process_count_high_watermark))
                Error("low or high watermark is not an unsigned number!");
            if (process_count_high_watermark > process_count_low_watermark)
                Error("the high watermark must be larger than the low watermark!");
            ++argv;
            argc -= 2;
        } else
            Error("unknown flag: " + std::string(*argv));
    }

    const std::string marc_input_filename(*argv++);
    const std::string marc_output_filename(*argv++);
    if (marc_input_filename == marc_output_filename)
        Error("input filename must not equal output filename!");

    std::unique_ptr<MarcReader> marc_reader(MarcReader::Factory(marc_input_filename, MarcReader::BINARY));
    std::unique_ptr<MarcWriter> marc_writer(MarcWriter::Factory(marc_output_filename, MarcWriter::BINARY));

    const std::string db_filename(*argv++);
    kyotocabinet::HashDB db;
    if (not db.open(db_filename, kyotocabinet::HashDB::OWRITER | kyotocabinet::HashDB::OCREATE))
        Error("Failed to create the key/valuedatabase \"" + db_filename + "\" ("
              + std::string(db.error().message()) + ")!");
    db.close();

    const std::string error_log_path(*argv);
    const std::string UPDATE_DB_LOG_DIR_PATH(
        "/var/log/" + std::string(FileUtil::Exists("/var/log/krimdok") ? "krimdok" : "ixtheo")
        + "/update_full_text_db");
    if (not FileUtil::MakeDirectory(UPDATE_DB_LOG_DIR_PATH, /* recursive = */ true))
        Error("failed to create directory: " + UPDATE_DB_LOG_DIR_PATH);

    try {
        ProcessRecords(max_record_count, skip_count, marc_reader.get(), marc_writer.get(), db_filename,
                       error_log_path, process_count_low_watermark, process_count_high_watermark);
    } catch (const std::exception &e) {
        Error("Caught exception: " + std::string(e.what()));
    }
}
<commit_msg>Rotate the error logs.<commit_after>/** \brief Utility for augmenting MARC records with links to a local full-text database.
 *  \author Dr. Johannes Ruscheinski (johannes.ruscheinski@uni-tuebingen.de)
 *
 *  \copyright 2015-2017 Universitätsbibliothek Tübingen.  All rights reserved.
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU Affero General Public License as
 *  published by the Free Software Foundation, either version 3 of the
 *  License, or (at your option) any later version.
 *
 *  This program 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 Affero General Public License for more details.
 *
 *  You should have received a copy of the GNU Affero General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <algorithm>
#include <iostream>
#include <map>
#include <memory>
#include <stdexcept>
#include <vector>
#include <climits>
#include <cstdio>
#include <cstdlib>
#include <libgen.h>
#include <strings.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <kchashdb.h>
#include "Downloader.h"
#include "ExecUtil.h"
#include "FileUtil.h"
#include "MarcReader.h"
#include "MarcRecord.h"
#include "MarcUtil.h"
#include "MarcWriter.h"
#include "MiscUtil.h"
#include "RegexMatcher.h"
#include "Semaphore.h"
#include "StringUtil.h"
#include "Subfields.h"
#include "TimeLimit.h"
#include "util.h"

static void Usage() __attribute__((noreturn));

static void Usage() {
    std::cerr << "Usage: " << ::progname
              << "[--max-record-count count] [--skip-count count] [--process-count-low-and-high-watermarks low:high] "
              << "marc_input marc_output full_text_db error_log_path\n"
              << "       --process-count-low-and-high-watermarks sets the maximum and minimum number of spawned\n"
              << "       child processes.  When we hit the high water mark we wait for child processes to exit\n"
              << "       until we reach the low watermark.\n\n";

    std::exit(EXIT_FAILURE);
}

// Checks subfields "3" and "z" to see if they start w/ "Rezension".
bool IsProbablyAReview(const Subfields &subfields) {
    const auto &_3_begin_end(subfields.getIterators('3'));
    if (_3_begin_end.first != _3_begin_end.second) {
        if (StringUtil::StartsWith(_3_begin_end.first->value_, "Rezension"))
            return true;
    } else {
        const auto &z_begin_end(subfields.getIterators('z'));
        if (z_begin_end.first != z_begin_end.second
            and StringUtil::StartsWith(z_begin_end.first->value_, "Rezension"))
            return true;
    }

    return false;
}

bool FoundAtLeastOneNonReviewLink(const MarcRecord &record) {
    for (size_t _856_index(record.getFieldIndex("856")); record.getTag(_856_index) == "856"; ++_856_index) {
        const Subfields subfields(record.getSubfields(_856_index));
        if (subfields.getIndicator1() == '7' or not subfields.hasSubfield('u'))
            continue;

        if (not IsProbablyAReview(subfields))
            return true;
    }

    return false;
}

// Returns the number of child processes that returned a non-zero exit code.
unsigned CleanUpZombies(const unsigned zombies_to_collect) {
    unsigned child_reported_failure_count(0);
    for (unsigned zombie_no(0); zombie_no < zombies_to_collect; ++zombie_no) {
        int exit_code;
        ::wait(&exit_code);
        if (exit_code != 0)
            ++child_reported_failure_count;
    }

    return child_reported_failure_count;
}

void ProcessRecords(const unsigned max_record_count, const unsigned skip_count, MarcReader * const marc_reader,
                    MarcWriter * const marc_writer, const std::string &db_filename, const std::string &error_log_path,
                    const unsigned process_count_low_watermark, const unsigned process_count_high_watermark)
{
    Semaphore semaphore("/full_text_cached_counter", Semaphore::CREATE);
    std::string err_msg;
    unsigned total_record_count(0), spawn_count(0), active_child_count(0), child_reported_failure_count(0);

    const std::string UPDATE_FULL_TEXT_DB_PATH("/usr/local/bin/update_full_text_db");

    std::cout << "Skip " << skip_count << " records\n";
    off_t record_start = marc_reader->tell();
    while (MarcRecord record = marc_reader->read()) {
        if (total_record_count == max_record_count)
            break;
        ++total_record_count;
        if (total_record_count <= skip_count) {
            record_start = marc_reader->tell();
            continue;
        }

        const bool insert_in_cache(FoundAtLeastOneNonReviewLink(record)
                                   or (not MarcUtil::HasTagAndSubfield(record, "856", 'u')
                                       and MarcUtil::HasTagAndSubfield(record, "520", 'a')));
        if (not insert_in_cache) {
            MarcUtil::FileLockedComposeAndWriteRecord(marc_writer, &record);
            record_start = marc_reader->tell();
            continue;
        }

        ExecUtil::Spawn(UPDATE_FULL_TEXT_DB_PATH,
                        { std::to_string(record_start), marc_reader->getPath(), marc_writer->getFile().getPath(),
                                db_filename, error_log_path });
        ++active_child_count;
        ++spawn_count;

        if (active_child_count > process_count_high_watermark) {
            child_reported_failure_count += CleanUpZombies(active_child_count - process_count_low_watermark);
            active_child_count = process_count_low_watermark;
        }
        record_start = marc_reader->tell();
    }

    // Wait for stragglers:
    child_reported_failure_count += CleanUpZombies(active_child_count);

    if (not err_msg.empty())
        Error(err_msg);
    std::cerr << "Read " << total_record_count << " records.\n";
    std::cerr << "Spawned " << spawn_count << " subprocesses.\n";
    std::cerr << semaphore.getValue()
              << " documents were not downloaded because their cached values had not yet expired.\n";
    std::cerr << child_reported_failure_count << " children reported a failure!\n";
}

constexpr unsigned PROCESS_COUNT_DEFAULT_HIGH_WATERMARK(10);
constexpr unsigned PROCESS_COUNT_DEFAULT_LOW_WATERMARK(5);

int main(int argc, char **argv) {
    ::progname = argv[0];

    if (argc != 5 and argc != 7 and argc != 9 and argc != 11)
        Usage();
    ++argv; // skip program name

    // Process optional args:
    unsigned max_record_count(UINT_MAX), skip_count(0);
    unsigned process_count_low_watermark(PROCESS_COUNT_DEFAULT_LOW_WATERMARK),
             process_count_high_watermark(PROCESS_COUNT_DEFAULT_HIGH_WATERMARK);
    while (argc > 5) {
        std::cout << "Arg: " << *argv << "\n";
        if (std::strcmp(*argv, "--max-record-count") == 0) {
            ++argv;
            if (not StringUtil::ToNumber(*argv, &max_record_count) or max_record_count == 0)
                Error("bad value for --max-record-count!");
            ++argv;
            argc -= 2;
        } else if (std::strcmp(*argv, "--skip-count") == 0) {
            ++argv;
            if (not StringUtil::ToNumber(*argv, &skip_count))
                Error("bad value for --skip-count!");
            std::cout << "Should skip " << skip_count << " records\n";
            ++argv;
            argc -= 2;
        } else if (std::strcmp("--process-count-low-and-high-watermarks", *argv) == 0) {
            ++argv;
            char *arg_end(*argv + std::strlen(*argv));
            char * const colon(std::find(*argv, arg_end, ':'));
            if (colon == arg_end)
                Error("bad argument to --process-count-low-and-high-watermarks: colon is missing!");
            *colon = '\0';
            if (not StringUtil::ToNumber(*argv, &process_count_low_watermark)
                or not StringUtil::ToNumber(*argv, &process_count_high_watermark))
                Error("low or high watermark is not an unsigned number!");
            if (process_count_high_watermark > process_count_low_watermark)
                Error("the high watermark must be larger than the low watermark!");
            ++argv;
            argc -= 2;
        } else
            Error("unknown flag: " + std::string(*argv));
    }

    const std::string marc_input_filename(*argv++);
    const std::string marc_output_filename(*argv++);
    if (marc_input_filename == marc_output_filename)
        Error("input filename must not equal output filename!");

    std::unique_ptr<MarcReader> marc_reader(MarcReader::Factory(marc_input_filename, MarcReader::BINARY));
    std::unique_ptr<MarcWriter> marc_writer(MarcWriter::Factory(marc_output_filename, MarcWriter::BINARY));

    const std::string db_filename(*argv++);
    kyotocabinet::HashDB db;
    if (not db.open(db_filename, kyotocabinet::HashDB::OWRITER | kyotocabinet::HashDB::OCREATE))
        Error("Failed to create the key/valuedatabase \"" + db_filename + "\" ("
              + std::string(db.error().message()) + ")!");
    db.close();

    const std::string error_log_path(*argv);
    const std::string UPDATE_DB_LOG_DIR_PATH(
        "/var/log/" + std::string(FileUtil::Exists("/var/log/krimdok") ? "krimdok" : "ixtheo")
        + "/update_full_text_db");
    if (not FileUtil::MakeDirectory(UPDATE_DB_LOG_DIR_PATH, /* recursive = */ true))
        Error("failed to create directory: " + UPDATE_DB_LOG_DIR_PATH);

    try {
        MiscUtil::LogRotate(error_log_path, /* max_count = */ 5);
        ProcessRecords(max_record_count, skip_count, marc_reader.get(), marc_writer.get(), db_filename,
                       error_log_path, process_count_low_watermark, process_count_high_watermark);
    } catch (const std::exception &e) {
        Error("Caught exception: " + std::string(e.what()));
    }
}
<|endoftext|>