<commit_before>/*************************************************************************
 *
 *  $RCSfile: SwXMLBlockExport.hxx,v $
 *
 *  $Revision: 1.5 $
 *
 *  last change: $Author: obo $ $Date: 2005-04-13 09:42:20 $
 *
 *  The Contents of this file are made available subject to the terms of
 *  either of the following licenses
 *
 *         - GNU Lesser General Public License Version 2.1
 *         - Sun Industry Standards Source License Version 1.1
 *
 *  Sun Microsystems Inc., October, 2000
 *
 *  GNU Lesser General Public License Version 2.1
 *  =============================================
 *  Copyright 2000 by Sun Microsystems, Inc.
 *  901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public
 *  License version 2.1, as published by the Free Software Foundation.
 *
 *  This library is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this library; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 *  MA  02111-1307  USA
 *
 *
 *  Sun Industry Standards Source License Version 1.1
 *  =================================================
 *  The contents of this file are subject to the Sun Industry Standards
 *  Source License Version 1.1 (the "License"); You may not use this file
 *  except in compliance with the License. You may obtain a copy of the
 *  License at http://www.openoffice.org/license.html.
*
 *  Software provided under this License is provided on an "AS IS" basis,
 *  WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING,
 *  WITHOUT LIMITATION, WARRANTIES THAT THE SOFTWARE IS FREE OF DEFECTS,
 *  MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE, OR NON-INFRINGING.
 *  See the License for the specific provisions governing your rights and
 *  obligations concerning the Software.
 *
 *  The Initial Developer of the Original Code is: Sun Microsystems, Inc.
 *
 *  Copyright: 2000 by Sun Microsystems, Inc.
 *
 *  All Rights Reserved.
 *
 *  Contributor(s): Martin Gallwey (gallwey@sun.com)
 *
 *
 ************************************************************************/
#ifndef _SW_XMLBLOCKEXPORT_HXX
#define _SW_XMLBLOCKEXPORT_HXX

#ifndef _XMLOFF_XMLEXP_HXX
#include <xmloff/xmlexp.hxx>
#endif

class String;
class SwXMLTextBlocks;

class SwXMLBlockListExport : public SvXMLExport
{
private:
    SwXMLTextBlocks &rBlockList;
public:
    // #110680#
    SwXMLBlockListExport(
        const ::com::sun::star::uno::Reference< ::com::sun::star::lang::XMultiServiceFactory > xServiceFactory,
        SwXMLTextBlocks & rBlocks,
        const rtl::OUString &rFileName,
        com::sun::star::uno::Reference< com::sun::star::xml::sax::XDocumentHandler> &rHandler);

    virtual ~SwXMLBlockListExport ( void ) {}
    sal_uInt32 exportDoc( enum ::xmloff::token::XMLTokenEnum eClass);
    void _ExportAutoStyles() {}
    void _ExportMasterStyles () {}
    void _ExportContent() {}
};

class SwXMLTextBlockExport : public SvXMLExport
{
private:
    SwXMLTextBlocks &rBlockList;
public:
    // #110680#
    SwXMLTextBlockExport(
        const ::com::sun::star::uno::Reference< ::com::sun::star::lang::XMultiServiceFactory > xServiceFactory,
        SwXMLTextBlocks & rBlocks,
        const rtl::OUString &rFileName,
        com::sun::star::uno::Reference< com::sun::star::xml::sax::XDocumentHandler> &rHandler);

    virtual ~SwXMLTextBlockExport ( void ) {}
    sal_uInt32 exportDoc(enum ::xmloff::token::XMLTokenEnum eClass) {return 0;}
    sal_uInt32 exportDoc(const String & rText);
    void _ExportAutoStyles() {}
    void _ExportMasterStyles () {}
    void _ExportContent() {}
};
#endif
<commit_msg>INTEGRATION: CWS ooo19126 (1.5.192); FILE MERGED 2005/09/05 13:39:50 rt 1.5.192.1: #i54170# Change license header: remove SISSL<commit_after>/*************************************************************************
 *
 *  OpenOffice.org - a multi-platform office productivity suite
 *
 *  $RCSfile: SwXMLBlockExport.hxx,v $
 *
 *  $Revision: 1.6 $
 *
 *  last change: $Author: rt $ $Date: 2005-09-09 03:40:39 $
 *
 *  The Contents of this file are made available subject to
 *  the terms of GNU Lesser General Public License Version 2.1.
 *
 *
 *    GNU Lesser General Public License Version 2.1
 *    =============================================
 *    Copyright 2005 by Sun Microsystems, Inc.
 *    901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *    This library is free software; you can redistribute it and/or
 *    modify it under the terms of the GNU Lesser General Public
 *    License version 2.1, as published by the Free Software Foundation.
 *
 *    This library is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *    Lesser General Public License for more details.
 *
 *    You should have received a copy of the GNU Lesser General Public
 *    License along with this library; if not, write to the Free Software
 *    Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 *    MA  02111-1307  USA
 *
 ************************************************************************/
#ifndef _SW_XMLBLOCKEXPORT_HXX
#define _SW_XMLBLOCKEXPORT_HXX

#ifndef _XMLOFF_XMLEXP_HXX
#include <xmloff/xmlexp.hxx>
#endif

class String;
class SwXMLTextBlocks;

class SwXMLBlockListExport : public SvXMLExport
{
private:
    SwXMLTextBlocks &rBlockList;
public:
    // #110680#
    SwXMLBlockListExport(
        const ::com::sun::star::uno::Reference< ::com::sun::star::lang::XMultiServiceFactory > xServiceFactory,
        SwXMLTextBlocks & rBlocks,
        const rtl::OUString &rFileName,
        com::sun::star::uno::Reference< com::sun::star::xml::sax::XDocumentHandler> &rHandler);

    virtual ~SwXMLBlockListExport ( void ) {}
    sal_uInt32 exportDoc( enum ::xmloff::token::XMLTokenEnum eClass);
    void _ExportAutoStyles() {}
    void _ExportMasterStyles () {}
    void _ExportContent() {}
};

class SwXMLTextBlockExport : public SvXMLExport
{
private:
    SwXMLTextBlocks &rBlockList;
public:
    // #110680#
    SwXMLTextBlockExport(
        const ::com::sun::star::uno::Reference< ::com::sun::star::lang::XMultiServiceFactory > xServiceFactory,
        SwXMLTextBlocks & rBlocks,
        const rtl::OUString &rFileName,
        com::sun::star::uno::Reference< com::sun::star::xml::sax::XDocumentHandler> &rHandler);

    virtual ~SwXMLTextBlockExport ( void ) {}
    sal_uInt32 exportDoc(enum ::xmloff::token::XMLTokenEnum eClass) {return 0;}
    sal_uInt32 exportDoc(const String & rText);
    void _ExportAutoStyles() {}
    void _ExportMasterStyles () {}
    void _ExportContent() {}
};
#endif
<|endoftext|>


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

#include <affine_invariant_features/feature_parameters.hpp>

#include <opencv2/core.hpp>

int main(int argc, char *argv[]) {
  namespace aif = affine_invariant_features;

  if (argc != 3) {
    std::cout << "Usage: generate_parameter_file <parameter_type> <parameter_file>" << std::endl;
    std::cout << "Available parameter types: AKAZEParameters, BRISKParameters, SIFTParameters, ..."
              << std::endl;
    return 0;
  }

  const std::string type(argv[1]);
  const std::string path(argv[2]);

  const cv::Ptr< const aif::FeatureParameters > params(aif::createFeatureParameters(type));
  if (!params) {
    std::cerr << "Could not create parameter set whose type is " << type << std::endl;
    return 1;
  }

  cv::FileStorage file(path, cv::FileStorage::WRITE);
  file << params->getDefaultName() << *params;
  std::cout << "Wrote parameter set whose type is " << type << " to " << path << std::endl;

  return 0;
}
<commit_msg>refactor generate_parameter_file<commit_after>#include <iostream>
#include <string>

#include <affine_invariant_features/feature_parameters.hpp>

#include <opencv2/core.hpp>

int main(int argc, char *argv[]) {

  namespace aif = affine_invariant_features;

  const cv::CommandLineParser args(
      argc, argv,
      "{ help | | }"
      "{ @parameter-type | <none> | SIFTParameters, BRISKParameters, AKAZEParameters, ... }"
      "{ @parameter-file | <none> | }");

  if (args.has("help")) {
    args.printMessage();
    return 0;
  }

  const std::string type(args.get< std::string >("@parameter-type"));
  const std::string path(args.get< std::string >("@parameter-file"));
  if (!args.check()) {
    args.printErrors();
    return 1;
  }

  const cv::Ptr< const aif::FeatureParameters > params(aif::createFeatureParameters(type));
  if (!params) {
    std::cerr << "Could not create a parameter set whose type is " << type << std::endl;
    return 1;
  }

  cv::FileStorage file(path, cv::FileStorage::WRITE);
  if (!file.isOpened()) {
    std::cerr << "Could not open or create " << path << std::endl;
    return 1;
  }

  file << params->getDefaultName() << *params;
  std::cout << "Wrote a parameter set whose type is " << type << " to " << path << std::endl;

  return 0;
}
<|endoftext|>


<commit_before>#include "./object_manager.h"
#include <QLoggingCategory>
#include <vector>
#include <map>
#include <string>
#include <algorithm>
#include "./gl.h"
#include "./buffer_manager.h"
#include "./texture_manager.h"
#include "./shader_manager.h"

namespace Graphics
{

QLoggingCategory omChan("Graphics.ObjectManager");

ObjectManager::ObjectManager(std::shared_ptr<TextureManager> textureManager,
                             std::shared_ptr<ShaderManager> shaderManager)
  : bufferManager(std::make_shared<BufferManager>()),
    textureManager(textureManager), shaderManager(shaderManager),
    transformBuffer(GL_SHADER_STORAGE_BUFFER),
    customBuffer(GL_SHADER_STORAGE_BUFFER),
    commandsBuffer(GL_DRAW_INDIRECT_BUFFER)
{
}

ObjectManager::~ObjectManager()
{
  qCInfo(omChan) << "Destructor of ObjectManager";
}

void ObjectManager::initialize(Gl *gl, uint maxObjectCount, uint bufferSize)
{
  this->gl = gl;

  bufferManager->initialize(gl, maxObjectCount, bufferSize);

  commandsBuffer.initialize(gl, 3 * maxObjectCount, CREATE_FLAGS, MAP_FLAGS);
  transformBuffer.initialize(gl, 3 * maxObjectCount, CREATE_FLAGS, MAP_FLAGS);
  customBuffer.initialize(gl, 12 * maxObjectCount, CREATE_FLAGS, MAP_FLAGS);
}

ObjectData ObjectManager::addObject(const std::vector<float> &vertices,
                                    const std::vector<float> &normals,
                                    const std::vector<float> &colors,
                                    const std::vector<float> &texCoords,
                                    const std::vector<uint> &indices,
                                    int shaderProgramId, int primitiveType)
{
  auto bufferInformation =
      bufferManager->addObject(vertices, normals, colors, texCoords, indices);

  return ObjectData(nextFreeId++, bufferInformation.vertexBufferOffset,
                    bufferInformation.indexBufferOffset, indices.size(),
                    shaderProgramId, primitiveType);
}

ObjectData ObjectManager::cloneForDifferentShader(const ObjectData &object,
                                                  int shaderProgramId)
{
  return ObjectData(nextFreeId++, object.getVertexOffset(),
                    object.getIndexOffset(), object.getIndexSize(),
                    shaderProgramId, object.getPrimitiveType());
}

ObjectData ObjectManager::clone(const ObjectData &object)
{
  return ObjectData(nextFreeId++, object.getVertexOffset(),
                    object.getIndexOffset(), object.getIndexSize(),
                    object.getShaderProgramId(), object.getPrimitiveType());
}

void ObjectManager::render(const RenderData &renderData)
{
  std::map<int, std::vector<ObjectData>> objectsByShader;
  for (auto &object : objectsForFrame)
  {
    objectsByShader[object.getShaderProgramId()].push_back(object);
  }

  for (auto &shaderObjectPair : objectsByShader)
  {
    qCDebug(omChan) << "Render objects with shader" << shaderObjectPair.first
                    << "with" << shaderObjectPair.second.size() << "objects";

    shaderManager->bindForHABuffer(shaderObjectPair.first, renderData);

    std::map<int, std::vector<ObjectData>> objectsByPrimitiveType;
    for (auto &object : shaderObjectPair.second)
    {
      objectsByPrimitiveType[object.getPrimitiveType()].push_back(object);
    }

    for (auto pair : objectsByPrimitiveType)
    {
      renderObjects(objectsByPrimitiveType[pair.first]);
    }
  }

  objectsForFrame.clear();
}

void ObjectManager::renderImmediately(ObjectData objectData)
{
  std::vector<ObjectData> objs = { objectData };

  renderObjects(objs);
}

void ObjectManager::renderLater(ObjectData object)
{
  objectsForFrame.push_back(object);
}

void ObjectManager::renderObjects(std::vector<ObjectData> objects)
{
  bufferManager->bind();

  // prepare per object buffers
  uint objectCount = objects.size();
  DrawElementsIndirectCommand *commands = commandsBuffer.reserve(objectCount);
  auto *matrices = transformBuffer.reserve(objectCount);
  int customBufferSize = objects[0].getCustomBufferSize() * objectCount;

  void *custom = nullptr;
  if (customBufferSize)
    custom = customBuffer.reserve(customBufferSize);

  qCDebug(omChan) << "customBufferSize" << customBufferSize << "custom"
                  << custom << "matrices" << matrices;

  int counter = 0;
  for (auto &objectData : objects)
  {
    commands[counter] = createDrawCommand(objectData, counter);

    auto *transform = &matrices[counter];
    auto modelMatrix = objectData.modelMatrix;

    // encode objectId into modelMatrix
    int objectId = objectData.getId();
    modelMatrix(3, 0) = *reinterpret_cast<float *>(&objectId);

    memcpy(transform, modelMatrix.data(), sizeof(float[16]));

    if (objectData.hasCustomBuffer())
    {
      objectData.fillBufferElement(custom, counter);
    }

    ++counter;
  }

  transformBuffer.bindBufferRange(0, objectCount);
  if (customBufferSize)
    customBuffer.bindBufferRange(1, customBufferSize);

  // We didn't use MAP_COHERENT here - make sure data is on the gpu
  // glAssert(gl->glMemoryBarrier(GL_CLIENT_MAPPED_BUFFER_BARRIER_BIT));
  glAssert(gl->glMemoryBarrier(GL_ALL_BARRIER_BITS));

  // draw
  qCDebug(omChan, "head: %ld headoffset %p objectcount: %u",
          commandsBuffer.getHead(), commandsBuffer.headOffset(), objectCount);

  glAssert(gl->glMultiDrawElementsIndirect(
      objects[0].getPrimitiveType(), GL_UNSIGNED_INT,
      commandsBuffer.headOffset(), objectCount, 0));

  bufferManager->unbind();

  commandsBuffer.onUsageComplete(objectCount);
  transformBuffer.onUsageComplete(objectCount);
  if (customBufferSize)
    customBuffer.onUsageComplete(customBufferSize);
}

ObjectManager::DrawElementsIndirectCommand
ObjectManager::createDrawCommand(const ObjectData &objectData, int counter)
{
  DrawElementsIndirectCommand command;

  command.count = objectData.getIndexSize();
  command.instanceCount = 1;
  command.firstIndex = objectData.getIndexOffset();
  command.baseVertex = objectData.getVertexOffset();
  command.baseInstance = counter;

  qCDebug(omChan, "counter: %d count: %u firstIndex: %u baseVertex: %u",
          counter, command.count, command.firstIndex, command.baseVertex);

  return command;
}

}  // namespace Graphics
<commit_msg>Increase the custom buffer size so that the dice scene works.<commit_after>#include "./object_manager.h"
#include <QLoggingCategory>
#include <vector>
#include <map>
#include <string>
#include <algorithm>
#include "./gl.h"
#include "./buffer_manager.h"
#include "./texture_manager.h"
#include "./shader_manager.h"

namespace Graphics
{

QLoggingCategory omChan("Graphics.ObjectManager");

ObjectManager::ObjectManager(std::shared_ptr<TextureManager> textureManager,
                             std::shared_ptr<ShaderManager> shaderManager)
  : bufferManager(std::make_shared<BufferManager>()),
    textureManager(textureManager), shaderManager(shaderManager),
    transformBuffer(GL_SHADER_STORAGE_BUFFER),
    customBuffer(GL_SHADER_STORAGE_BUFFER),
    commandsBuffer(GL_DRAW_INDIRECT_BUFFER)
{
}

ObjectManager::~ObjectManager()
{
  qCInfo(omChan) << "Destructor of ObjectManager";
}

void ObjectManager::initialize(Gl *gl, uint maxObjectCount, uint bufferSize)
{
  this->gl = gl;

  bufferManager->initialize(gl, maxObjectCount, bufferSize);

  commandsBuffer.initialize(gl, 3 * maxObjectCount, CREATE_FLAGS, MAP_FLAGS);
  transformBuffer.initialize(gl, 3 * maxObjectCount, CREATE_FLAGS, MAP_FLAGS);
  customBuffer.initialize(gl, 24 * maxObjectCount, CREATE_FLAGS, MAP_FLAGS);
}

ObjectData ObjectManager::addObject(const std::vector<float> &vertices,
                                    const std::vector<float> &normals,
                                    const std::vector<float> &colors,
                                    const std::vector<float> &texCoords,
                                    const std::vector<uint> &indices,
                                    int shaderProgramId, int primitiveType)
{
  auto bufferInformation =
      bufferManager->addObject(vertices, normals, colors, texCoords, indices);

  return ObjectData(nextFreeId++, bufferInformation.vertexBufferOffset,
                    bufferInformation.indexBufferOffset, indices.size(),
                    shaderProgramId, primitiveType);
}

ObjectData ObjectManager::cloneForDifferentShader(const ObjectData &object,
                                                  int shaderProgramId)
{
  return ObjectData(nextFreeId++, object.getVertexOffset(),
                    object.getIndexOffset(), object.getIndexSize(),
                    shaderProgramId, object.getPrimitiveType());
}

ObjectData ObjectManager::clone(const ObjectData &object)
{
  return ObjectData(nextFreeId++, object.getVertexOffset(),
                    object.getIndexOffset(), object.getIndexSize(),
                    object.getShaderProgramId(), object.getPrimitiveType());
}

void ObjectManager::render(const RenderData &renderData)
{
  std::map<int, std::vector<ObjectData>> objectsByShader;
  for (auto &object : objectsForFrame)
  {
    objectsByShader[object.getShaderProgramId()].push_back(object);
  }

  for (auto &shaderObjectPair : objectsByShader)
  {
    qCDebug(omChan) << "Render objects with shader" << shaderObjectPair.first
                    << "with" << shaderObjectPair.second.size() << "objects";

    shaderManager->bindForHABuffer(shaderObjectPair.first, renderData);

    std::map<int, std::vector<ObjectData>> objectsByPrimitiveType;
    for (auto &object : shaderObjectPair.second)
    {
      objectsByPrimitiveType[object.getPrimitiveType()].push_back(object);
    }

    for (auto pair : objectsByPrimitiveType)
    {
      renderObjects(objectsByPrimitiveType[pair.first]);
    }
  }

  objectsForFrame.clear();
}

void ObjectManager::renderImmediately(ObjectData objectData)
{
  std::vector<ObjectData> objs = { objectData };

  renderObjects(objs);
}

void ObjectManager::renderLater(ObjectData object)
{
  objectsForFrame.push_back(object);
}

void ObjectManager::renderObjects(std::vector<ObjectData> objects)
{
  bufferManager->bind();

  // prepare per object buffers
  uint objectCount = objects.size();
  DrawElementsIndirectCommand *commands = commandsBuffer.reserve(objectCount);
  auto *matrices = transformBuffer.reserve(objectCount);
  int customBufferSize = objects[0].getCustomBufferSize() * objectCount;

  void *custom = nullptr;
  if (customBufferSize)
    custom = customBuffer.reserve(customBufferSize);

  qCDebug(omChan) << "customBufferSize" << customBufferSize << "custom"
                  << custom << "matrices" << matrices;

  int counter = 0;
  for (auto &objectData : objects)
  {
    commands[counter] = createDrawCommand(objectData, counter);

    auto *transform = &matrices[counter];
    auto modelMatrix = objectData.modelMatrix;

    // encode objectId into modelMatrix
    int objectId = objectData.getId();
    modelMatrix(3, 0) = *reinterpret_cast<float *>(&objectId);

    memcpy(transform, modelMatrix.data(), sizeof(float[16]));

    if (objectData.hasCustomBuffer())
    {
      objectData.fillBufferElement(custom, counter);
    }

    ++counter;
  }

  transformBuffer.bindBufferRange(0, objectCount);
  if (customBufferSize)
    customBuffer.bindBufferRange(1, customBufferSize);

  // We didn't use MAP_COHERENT here - make sure data is on the gpu
  // glAssert(gl->glMemoryBarrier(GL_CLIENT_MAPPED_BUFFER_BARRIER_BIT));
  glAssert(gl->glMemoryBarrier(GL_ALL_BARRIER_BITS));

  // draw
  qCDebug(omChan, "head: %ld headoffset %p objectcount: %u",
          commandsBuffer.getHead(), commandsBuffer.headOffset(), objectCount);

  glAssert(gl->glMultiDrawElementsIndirect(
      objects[0].getPrimitiveType(), GL_UNSIGNED_INT,
      commandsBuffer.headOffset(), objectCount, 0));

  bufferManager->unbind();

  commandsBuffer.onUsageComplete(objectCount);
  transformBuffer.onUsageComplete(objectCount);
  if (customBufferSize)
    customBuffer.onUsageComplete(customBufferSize);
}

ObjectManager::DrawElementsIndirectCommand
ObjectManager::createDrawCommand(const ObjectData &objectData, int counter)
{
  DrawElementsIndirectCommand command;

  command.count = objectData.getIndexSize();
  command.instanceCount = 1;
  command.firstIndex = objectData.getIndexOffset();
  command.baseVertex = objectData.getVertexOffset();
  command.baseInstance = counter;

  qCDebug(omChan, "counter: %d count: %u firstIndex: %u baseVertex: %u",
          counter, command.count, command.firstIndex, command.baseVertex);

  return command;
}

}  // namespace Graphics
<|endoftext|>


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

/**
 * @file List.hpp
 *
 * A linked list.
 */

#pragma once

template<class T>
class __EXPORT ListNode
{
public:
	ListNode() : _sibling() {}
	virtual ~ListNode() = default;

	// no copy, assignment, move, move assignment
	ListNode(const ListNode &) = delete;
	ListNode &operator=(const ListNode &) = delete;
	ListNode(ListNode &&) = delete;
	ListNode &operator=(ListNode &&) = delete;

	void setSibling(T sibling) { _sibling = sibling; }
	const T getSibling() { return _sibling; }

protected:
	T _sibling;
};

template<class T>
class __EXPORT List
{
public:
	List() : _head() {}
	virtual ~List() = default;

	// no copy, assignment, move, move assignment
	List(const List &) = delete;
	List &operator=(const List &) = delete;
	List(List &&) = delete;
	List &operator=(List &&) = delete;

	void add(T newNode)
	{
		newNode->setSibling(getHead());
		_head = newNode;
	}

	const T getHead() { return _head; }

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

/**
 * @file List.hpp
 *
 * A linked list.
 */

#pragma once

template<class T>
class __EXPORT ListNode
{
public:
	ListNode() : _sibling() {}
	virtual ~ListNode() = default;

	// no copy, assignment, move, move assignment
	ListNode(const ListNode &) = delete;
	ListNode &operator=(const ListNode &) = delete;
	ListNode(ListNode &&) = delete;
	ListNode &operator=(ListNode &&) = delete;

	void setSibling(T sibling) { _sibling = sibling; }
	T getSibling() { return _sibling; }

protected:
	T _sibling;
};

template<class T>
class __EXPORT List
{
public:
	List() : _head() {}
	virtual ~List() = default;

	// no copy, assignment, move, move assignment
	List(const List &) = delete;
	List &operator=(const List &) = delete;
	List(List &&) = delete;
	List &operator=(List &&) = delete;

	void add(T newNode)
	{
		newNode->setSibling(getHead());
		_head = newNode;
	}

	T getHead() { return _head; }

protected:
	T _head;
};
<|endoftext|>


<commit_before>/*
 * This istream filter adds HTTP chunking.
 *
 * author: Max Kellermann <mk@cm4all.com>
 */

#include "istream_chunked.hxx"
#include "istream_oo.hxx"
#include "istream_pointer.hxx"
#include "istream_internal.hxx"
#include "pool.hxx"
#include "format.h"
#include "util/Cast.hxx"

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

struct ChunkedIstream {
    struct istream output;
    IstreamPointer input;

    /**
     * This flag is true while writing the buffer inside
     * istream_chunked_read().  chunked_input_data() will check it,
     * and refuse to accept more data from the input.  This avoids
     * writing the buffer recursively.
     */
    bool writing_buffer = false;

    char buffer[7];
    size_t buffer_sent = sizeof(buffer);

    size_t missing_from_current_chunk = 0;

    ChunkedIstream(struct pool &p, struct istream &_input);

    /* handler */
    size_t OnData(const void *data, size_t length);

    ssize_t OnDirect(gcc_unused FdType type, gcc_unused int fd,
                     gcc_unused size_t max_length) {
        gcc_unreachable();
    }

    void OnEof();
    void OnError(GError *error);

    bool IsBufferEmpty() const {
        assert(buffer_sent <= sizeof(buffer));

        return buffer_sent == sizeof(buffer);
    }

    /** set the buffer length and return a pointer to the first byte */
    char *SetBuffer(size_t length) {
        assert(IsBufferEmpty());
        assert(length <= sizeof(buffer));

        buffer_sent = sizeof(buffer) - length;
        return buffer + buffer_sent;
    }

    /** append data to the buffer */
    void AppendToBuffer(const void *data, size_t length);

    void StartChunk(size_t length);

    /**
     * Returns true if the buffer is consumed.
     */
    bool SendBuffer();

    /**
     * Wrapper for SendBuffer() that sets and clears the
     * writing_buffer flag.  This requires acquiring a pool reference
     * to do that safely.
     *
     * @return true if the buffer is consumed.
     */
    bool SendBuffer2();

    size_t Feed(const char *data, size_t length);
};

void
ChunkedIstream::AppendToBuffer(const void *data, size_t length)
{
    assert(data != nullptr);
    assert(length > 0);
    assert(length <= buffer_sent);

    const void *old = buffer + buffer_sent;
    size_t old_length = sizeof(buffer) - buffer_sent;

#ifndef NDEBUG
    /* simulate a buffer reset; if we don't do this, an assertion in
       chunked_buffer_set() fails (which is invalid for this special
       case) */
    buffer_sent = sizeof(buffer);
#endif

    auto dest = SetBuffer(old_length + length);
    memmove(dest, old, old_length);
    dest += old_length;

    memcpy(dest, data, length);
}

void
ChunkedIstream::StartChunk(size_t length)
{
    assert(length > 0);
    assert(IsBufferEmpty());
    assert(missing_from_current_chunk == 0);

    if (length > 0x8000)
        /* maximum chunk size is 32kB for now */
        length = 0x8000;

    missing_from_current_chunk = length;

    auto p = SetBuffer(6);
    format_uint16_hex_fixed(p, (uint16_t)length);
    p[4] = '\r';
    p[5] = '\n';
}

bool
ChunkedIstream::SendBuffer()
{
    size_t length = sizeof(buffer) - buffer_sent;
    if (length == 0)
        return true;

    size_t nbytes = istream_invoke_data(&output, buffer + buffer_sent, length);
    if (nbytes > 0)
        buffer_sent += nbytes;

    return nbytes == length;
}

bool
ChunkedIstream::SendBuffer2()
{
    const ScopePoolRef ref(*output.pool TRACE_ARGS);

    assert(!writing_buffer);
    writing_buffer = true;

    const bool result = SendBuffer();
    writing_buffer = false;
    return result;
}

inline size_t
ChunkedIstream::Feed(const char *data, size_t length)
{
    size_t total = 0, rest, nbytes;

    assert(input.IsDefined());

    do {
        assert(!writing_buffer);

        if (IsBufferEmpty() && missing_from_current_chunk == 0)
            StartChunk(length - total);

        if (!SendBuffer())
            return input.IsDefined() ? total : 0;

        assert(IsBufferEmpty());

        if (missing_from_current_chunk == 0) {
            /* we have just written the previous chunk trailer;
               re-start this loop to start a new chunk */
            nbytes = rest = 0;
            continue;
        }

        rest = length - total;
        if (rest > missing_from_current_chunk)
            rest = missing_from_current_chunk;

        nbytes = istream_invoke_data(&output, data + total, rest);
        if (nbytes == 0)
            return input.IsDefined() ? total : 0;

        total += nbytes;

        missing_from_current_chunk -= nbytes;
        if (missing_from_current_chunk == 0) {
            /* a chunk ends with "\r\n" */
            char *p = SetBuffer(2);
            p[0] = '\r';
            p[1] = '\n';
        }
    } while ((!IsBufferEmpty() || total < length) && nbytes == rest);

    return total;
}

/*
 * istream handler
 *
 */

size_t
ChunkedIstream::OnData(const void *data, size_t length)
{
    if (writing_buffer)
        /* this is a recursive call from istream_chunked_read(): bail
           out */
        return 0;

    const ScopePoolRef ref(*output.pool TRACE_ARGS);
    return Feed((const char*)data, length);
}

void
ChunkedIstream::OnEof()
{
    assert(input.IsDefined());
    assert(missing_from_current_chunk == 0);

    input.Clear();

    /* write EOF chunk (length 0) */

    AppendToBuffer("0\r\n\r\n", 5);

    /* flush the buffer */

    if (SendBuffer())
        istream_deinit_eof(&output);
}

void
ChunkedIstream::OnError(GError *error)
{
    assert(input.IsDefined());

    input.Clear();
    istream_deinit_abort(&output, error);
}

/*
 * istream implementation
 *
 */

static inline ChunkedIstream *
istream_to_chunked(struct istream *istream)
{
    return &ContainerCast2(*istream, &ChunkedIstream::output);
}

static void
istream_chunked_read(struct istream *istream)
{
    ChunkedIstream *chunked = istream_to_chunked(istream);

    if (!chunked->SendBuffer2())
        return;

    if (!chunked->input.IsDefined()) {
        istream_deinit_eof(&chunked->output);
        return;
    }

    if (chunked->IsBufferEmpty() &&
        chunked->missing_from_current_chunk == 0) {
        off_t available = chunked->input.GetAvailable(true);
        if (available > 0) {
            chunked->StartChunk(available);
            if (!chunked->SendBuffer2())
                return;
        }
    }

    chunked->input.Read();
}

static void
istream_chunked_close(struct istream *istream)
{
    ChunkedIstream *chunked = istream_to_chunked(istream);

    if (chunked->input.IsDefined())
        chunked->input.ClearAndClose();

    istream_deinit(&chunked->output);
}

static constexpr struct istream_class istream_chunked = {
    .read = istream_chunked_read,
    .close = istream_chunked_close,
};

/*
 * constructor
 *
 */

inline ChunkedIstream::ChunkedIstream(struct pool &p, struct istream &_input)
    :input(_input, MakeIstreamHandler<ChunkedIstream>::handler, this)
{
    istream_init(&output, &istream_chunked, &p);
}

struct istream *
istream_chunked_new(struct pool *pool, struct istream *input)
{
    assert(input != nullptr);
    assert(!istream_has_handler(input));

    auto *chunked = NewFromPool<ChunkedIstream>(*pool, *pool, *input);
    return &chunked->output;
}
<commit_msg>istream/chunked: use the OO API<commit_after>/*
 * This istream filter adds HTTP chunking.
 *
 * author: Max Kellermann <mk@cm4all.com>
 */

#include "istream_chunked.hxx"
#include "FacadeIstream.hxx"
#include "format.h"

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

class ChunkedIstream final : public FacadeIstream {
    /**
     * This flag is true while writing the buffer inside
     * istream_chunked_read().  chunked_input_data() will check it,
     * and refuse to accept more data from the input.  This avoids
     * writing the buffer recursively.
     */
    bool writing_buffer = false;

    char buffer[7];
    size_t buffer_sent = sizeof(buffer);

    size_t missing_from_current_chunk = 0;

public:
    ChunkedIstream(struct pool &p, struct istream &_input)
        :FacadeIstream(p, _input,
                       MakeIstreamHandler<ChunkedIstream>::handler, this) {}

    /* virtual methods from class Istream */

    void Read() override;
    void Close() override;

    /* handler */
    size_t OnData(const void *data, size_t length);

    ssize_t OnDirect(gcc_unused FdType type, gcc_unused int fd,
                     gcc_unused size_t max_length) {
        gcc_unreachable();
    }

    void OnEof();
    void OnError(GError *error);

private:
    bool IsBufferEmpty() const {
        assert(buffer_sent <= sizeof(buffer));

        return buffer_sent == sizeof(buffer);
    }

    /** set the buffer length and return a pointer to the first byte */
    char *SetBuffer(size_t length) {
        assert(IsBufferEmpty());
        assert(length <= sizeof(buffer));

        buffer_sent = sizeof(buffer) - length;
        return buffer + buffer_sent;
    }

    /** append data to the buffer */
    void AppendToBuffer(const void *data, size_t length);

    void StartChunk(size_t length);

    /**
     * Returns true if the buffer is consumed.
     */
    bool SendBuffer();

    /**
     * Wrapper for SendBuffer() that sets and clears the
     * writing_buffer flag.  This requires acquiring a pool reference
     * to do that safely.
     *
     * @return true if the buffer is consumed.
     */
    bool SendBuffer2();

    size_t Feed(const char *data, size_t length);
};

void
ChunkedIstream::AppendToBuffer(const void *data, size_t length)
{
    assert(data != nullptr);
    assert(length > 0);
    assert(length <= buffer_sent);

    const void *old = buffer + buffer_sent;
    size_t old_length = sizeof(buffer) - buffer_sent;

#ifndef NDEBUG
    /* simulate a buffer reset; if we don't do this, an assertion in
       chunked_buffer_set() fails (which is invalid for this special
       case) */
    buffer_sent = sizeof(buffer);
#endif

    auto dest = SetBuffer(old_length + length);
    memmove(dest, old, old_length);
    dest += old_length;

    memcpy(dest, data, length);
}

void
ChunkedIstream::StartChunk(size_t length)
{
    assert(length > 0);
    assert(IsBufferEmpty());
    assert(missing_from_current_chunk == 0);

    if (length > 0x8000)
        /* maximum chunk size is 32kB for now */
        length = 0x8000;

    missing_from_current_chunk = length;

    auto p = SetBuffer(6);
    format_uint16_hex_fixed(p, (uint16_t)length);
    p[4] = '\r';
    p[5] = '\n';
}

bool
ChunkedIstream::SendBuffer()
{
    size_t length = sizeof(buffer) - buffer_sent;
    if (length == 0)
        return true;

    size_t nbytes = InvokeData(buffer + buffer_sent, length);
    if (nbytes > 0)
        buffer_sent += nbytes;

    return nbytes == length;
}

bool
ChunkedIstream::SendBuffer2()
{
    const ScopePoolRef ref(GetPool() TRACE_ARGS);

    assert(!writing_buffer);
    writing_buffer = true;

    const bool result = SendBuffer();
    writing_buffer = false;
    return result;
}

inline size_t
ChunkedIstream::Feed(const char *data, size_t length)
{
    size_t total = 0, rest, nbytes;

    assert(input.IsDefined());

    do {
        assert(!writing_buffer);

        if (IsBufferEmpty() && missing_from_current_chunk == 0)
            StartChunk(length - total);

        if (!SendBuffer())
            return input.IsDefined() ? total : 0;

        assert(IsBufferEmpty());

        if (missing_from_current_chunk == 0) {
            /* we have just written the previous chunk trailer;
               re-start this loop to start a new chunk */
            nbytes = rest = 0;
            continue;
        }

        rest = length - total;
        if (rest > missing_from_current_chunk)
            rest = missing_from_current_chunk;

        nbytes = InvokeData(data + total, rest);
        if (nbytes == 0)
            return input.IsDefined() ? total : 0;

        total += nbytes;

        missing_from_current_chunk -= nbytes;
        if (missing_from_current_chunk == 0) {
            /* a chunk ends with "\r\n" */
            char *p = SetBuffer(2);
            p[0] = '\r';
            p[1] = '\n';
        }
    } while ((!IsBufferEmpty() || total < length) && nbytes == rest);

    return total;
}

/*
 * istream handler
 *
 */

size_t
ChunkedIstream::OnData(const void *data, size_t length)
{
    if (writing_buffer)
        /* this is a recursive call from istream_chunked_read(): bail
           out */
        return 0;

    const ScopePoolRef ref(GetPool() TRACE_ARGS);
    return Feed((const char*)data, length);
}

void
ChunkedIstream::OnEof()
{
    assert(input.IsDefined());
    assert(missing_from_current_chunk == 0);

    input.Clear();

    /* write EOF chunk (length 0) */

    AppendToBuffer("0\r\n\r\n", 5);

    /* flush the buffer */

    if (SendBuffer())
        DestroyEof();
}

void
ChunkedIstream::OnError(GError *error)
{
    assert(input.IsDefined());

    input.Clear();
    DestroyError(error);
}

/*
 * istream implementation
 *
 */

void
ChunkedIstream::Read()
{
    if (!SendBuffer2())
        return;

    if (!input.IsDefined()) {
        DestroyEof();
        return;
    }

    if (IsBufferEmpty() && missing_from_current_chunk == 0) {
        off_t available = input.GetAvailable(true);
        if (available > 0) {
            StartChunk(available);
            if (!SendBuffer2())
                return;
        }
    }

    input.Read();
}

void
ChunkedIstream::Close()
{
    if (input.IsDefined())
        input.ClearAndClose();

    Destroy();
}

/*
 * constructor
 *
 */

struct istream *
istream_chunked_new(struct pool *pool, struct istream *input)
{
    assert(input != nullptr);
    assert(!istream_has_handler(input));

    return NewIstream<ChunkedIstream>(*pool, *input);
}
<|endoftext|>


<commit_before>/*
 * This istream filter removes HTTP chunking.
 *
 * author: Max Kellermann <mk@cm4all.com>
 */

#include "istream_dechunk.hxx"
#include "http/ChunkParser.hxx"
#include "FacadeIstream.hxx"
#include "pool.hxx"

#include <algorithm>

#include <glib.h>

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

class DechunkIstream final : public FacadeIstream {
    HttpChunkParser parser;

    bool eof = false, closed = false;

    bool had_input, had_output;

    /**
     * Copy chunked data verbatim to handler?
     *
     * @see istream_dechunk_check_verbatim()
     */
    bool verbatim = false;

    /**
     * Was the end-of-file chunk seen at the end of #pending_verbatim?
     */
    bool eof_verbatim;

    /**
     * Number of bytes to be passed to handler verbatim, which have
     * already been parsed but have not yet been consumed by the
     * handler.
     */
    size_t pending_verbatim;

    DechunkHandler &dechunk_handler;

public:
    DechunkIstream(struct pool &p, Istream &_input,
                   DechunkHandler &_dechunk_handler)
        :FacadeIstream(p, _input), dechunk_handler(_dechunk_handler)
    {
    }

    static DechunkIstream *CheckCast(Istream &i) {
        return dynamic_cast<DechunkIstream *>(&i);
    }

    void SetVerbatim() {
        verbatim = true;
        eof_verbatim = false;
        pending_verbatim = 0;
    }

private:
    void Abort(GError *error);

    /**
     * @return false if the istream_dechunk has been aborted
     * indirectly (by a callback)
     */
    bool EofDetected();

    size_t Feed(const void *data, size_t length);

public:
    /* virtual methods from class Istream */

    off_t _GetAvailable(bool partial) override;
    void _Read() override;
    void _Close() override;

protected:
    /* virtual methods from class IstreamHandler */
    size_t OnData(const void *data, size_t length) override;
    void OnEof() override;
    void OnError(GError *error) override;
};

void
DechunkIstream::Abort(GError *error)
{
    assert(!closed);
    assert(!parser.HasEnded());
    assert(input.IsDefined());

    closed = true;

    input.ClearAndClose();
    DestroyError(error);
}

bool
DechunkIstream::EofDetected()
{
    assert(input.IsDefined());
    assert(parser.HasEnded());
    assert(!eof);

    eof = true;

    dechunk_handler.OnDechunkEnd();

    assert(input.IsDefined());
    assert(parser.HasEnded());

    const ScopePoolRef ref(GetPool() TRACE_ARGS);
    DestroyEof();

    if (closed) {
        assert(!input.IsDefined());

        return false;
    } else {
        /* we must deinitialize the "input" after emitting "eof",
           because we must give the callback a chance to call
           dechunk_input_abort() on us; if we'd clear the handler too
           early, we wouldn't receive that event, and
           dechunk_input_data() couldn't change its return value to
           0 */
        assert(input.IsDefined());

        input.ClearHandler();
        return true;
    }
}

size_t
DechunkIstream::Feed(const void *data0, size_t length)
{
    assert(!closed);
    assert(input.IsDefined());
    assert(!verbatim || !eof_verbatim);

    had_input = true;

    const auto src_begin = (const char *)data0;
    const auto src_end = src_begin + length;

    auto src = src_begin;
    if (verbatim)
        /* skip the part that has already been parsed in the last
           invocation, but could not be consumed by the handler */
        src += pending_verbatim;

    while (src != src_end) {
        GError *error = nullptr;
        const ConstBuffer<char> src_remaining(src, src_end - src);
        auto data = ConstBuffer<char>::FromVoid(parser.Parse(src_remaining.ToVoid(),
                                                             &error));
        if (data.IsNull()) {
            Abort(error);
            return 0;
        }

        assert(data.data >= src);
        assert(data.data <= src_end);
        assert(data.end() <= src_end);

        src = data.data;

        if (!data.IsEmpty()) {
            assert(!parser.HasEnded());

            size_t nbytes;

            if (verbatim) {
                /* postpone this data chunk; try to send it all later in
                   one big block */
                nbytes = data.size;
            } else {
                had_output = true;
                nbytes = InvokeData(src, data.size);
                assert(nbytes <= data.size);

                if (nbytes == 0)
                    return closed ? 0 : src - src_begin;
            }

            src += nbytes;

            bool finished = parser.Consume(nbytes);
            if (!finished && !verbatim)
                break;
        } else if (parser.HasEnded()) {
            break;
        } else {
            assert(src == src_end);
        }
    }

    const size_t position = src - src_begin;
    if (verbatim && position > 0) {
        /* send all chunks in one big block */
        had_output = true;
        size_t nbytes = InvokeData(src_begin, position);
        if (closed)
            return 0;

        /* postpone the rest that was not handled; it will not be
           parsed again */
        pending_verbatim = position - nbytes;
        if (parser.HasEnded()) {
                if (pending_verbatim > 0)
                    /* not everything could be sent; postpone to
                       next call */
                    eof_verbatim = true;
                else if (!EofDetected())
                    return 0;
        }

        return nbytes;
    } else if (parser.HasEnded() && !EofDetected())
        return 0;

    return position;
}

/*
 * istream handler
 *
 */

size_t
DechunkIstream::OnData(const void *data, size_t length)
{
    assert(!verbatim || length >= pending_verbatim);

    if (verbatim && eof_verbatim) {
        /* during the last call, the EOF chunk was parsed, but we
           could not handle it yet, because the handler did not
           consume all data yet; try to send the remaining pre-EOF
           data again and then handle the EOF chunk */

        assert(pending_verbatim > 0);

        assert(length >= pending_verbatim);

        had_output = true;
        size_t nbytes = InvokeData(data, pending_verbatim);
        if (nbytes == 0)
            return 0;

        pending_verbatim -= nbytes;
        if (pending_verbatim > 0)
            /* more data needed */
            return nbytes;

        return EofDetected() ? nbytes : 0;
    }

    const ScopePoolRef ref(GetPool() TRACE_ARGS);
    return Feed(data, length);
}

void
DechunkIstream::OnEof()
{
    assert(!closed);

    input.Clear();

    closed = true;

    if (eof)
        return;

    GError *error =
        g_error_new_literal(dechunk_quark(), 0,
                            "premature EOF in dechunker");
    DestroyError(error);
}

void
DechunkIstream::OnError(GError *error)
{
    input.Clear();

    closed = true;

    if (eof)
        g_error_free(error);
    else
        DestroyError(error);
}

/*
 * istream implementation
 *
 */

off_t
DechunkIstream::_GetAvailable(bool partial)
{
    if (partial)
        return (off_t)parser.GetAvailable();

    return (off_t)-1;
}

void
DechunkIstream::_Read()
{
    const ScopePoolRef ref(GetPool() TRACE_ARGS);

    had_output = false;

    do {
        had_input = false;
        input.Read();
    } while (input.IsDefined() && had_input && !had_output);
}

void
DechunkIstream::_Close()
{
    assert(!eof);
    assert(!closed);

    closed = true;

    input.ClearAndClose();
    Destroy();
}

/*
 * constructor
 *
 */

Istream *
istream_dechunk_new(struct pool *pool, Istream &input,
                    DechunkHandler &dechunk_handler)
{
    return NewIstream<DechunkIstream>(*pool, input, dechunk_handler);
}

bool
istream_dechunk_check_verbatim(Istream &i)
{
    auto *dechunk = DechunkIstream::CheckCast(i);
    if (dechunk == nullptr)
        /* not a DechunkIstream instance */
        return false;

    dechunk->SetVerbatim();
    return true;
}
<commit_msg>istream/dechunk: eliminate another "return" statement<commit_after>/*
 * This istream filter removes HTTP chunking.
 *
 * author: Max Kellermann <mk@cm4all.com>
 */

#include "istream_dechunk.hxx"
#include "http/ChunkParser.hxx"
#include "FacadeIstream.hxx"
#include "pool.hxx"

#include <algorithm>

#include <glib.h>

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

class DechunkIstream final : public FacadeIstream {
    HttpChunkParser parser;

    bool eof = false, closed = false;

    bool had_input, had_output;

    /**
     * Copy chunked data verbatim to handler?
     *
     * @see istream_dechunk_check_verbatim()
     */
    bool verbatim = false;

    /**
     * Was the end-of-file chunk seen at the end of #pending_verbatim?
     */
    bool eof_verbatim;

    /**
     * Number of bytes to be passed to handler verbatim, which have
     * already been parsed but have not yet been consumed by the
     * handler.
     */
    size_t pending_verbatim;

    DechunkHandler &dechunk_handler;

public:
    DechunkIstream(struct pool &p, Istream &_input,
                   DechunkHandler &_dechunk_handler)
        :FacadeIstream(p, _input), dechunk_handler(_dechunk_handler)
    {
    }

    static DechunkIstream *CheckCast(Istream &i) {
        return dynamic_cast<DechunkIstream *>(&i);
    }

    void SetVerbatim() {
        verbatim = true;
        eof_verbatim = false;
        pending_verbatim = 0;
    }

private:
    void Abort(GError *error);

    /**
     * @return false if the istream_dechunk has been aborted
     * indirectly (by a callback)
     */
    bool EofDetected();

    size_t Feed(const void *data, size_t length);

public:
    /* virtual methods from class Istream */

    off_t _GetAvailable(bool partial) override;
    void _Read() override;
    void _Close() override;

protected:
    /* virtual methods from class IstreamHandler */
    size_t OnData(const void *data, size_t length) override;
    void OnEof() override;
    void OnError(GError *error) override;
};

void
DechunkIstream::Abort(GError *error)
{
    assert(!closed);
    assert(!parser.HasEnded());
    assert(input.IsDefined());

    closed = true;

    input.ClearAndClose();
    DestroyError(error);
}

bool
DechunkIstream::EofDetected()
{
    assert(input.IsDefined());
    assert(parser.HasEnded());
    assert(!eof);

    eof = true;

    dechunk_handler.OnDechunkEnd();

    assert(input.IsDefined());
    assert(parser.HasEnded());

    const ScopePoolRef ref(GetPool() TRACE_ARGS);
    DestroyEof();

    if (closed) {
        assert(!input.IsDefined());

        return false;
    } else {
        /* we must deinitialize the "input" after emitting "eof",
           because we must give the callback a chance to call
           dechunk_input_abort() on us; if we'd clear the handler too
           early, we wouldn't receive that event, and
           dechunk_input_data() couldn't change its return value to
           0 */
        assert(input.IsDefined());

        input.ClearHandler();
        return true;
    }
}

size_t
DechunkIstream::Feed(const void *data0, size_t length)
{
    assert(!closed);
    assert(input.IsDefined());
    assert(!verbatim || !eof_verbatim);

    had_input = true;

    const auto src_begin = (const char *)data0;
    const auto src_end = src_begin + length;

    auto src = src_begin;
    if (verbatim)
        /* skip the part that has already been parsed in the last
           invocation, but could not be consumed by the handler */
        src += pending_verbatim;

    while (src != src_end) {
        GError *error = nullptr;
        const ConstBuffer<char> src_remaining(src, src_end - src);
        auto data = ConstBuffer<char>::FromVoid(parser.Parse(src_remaining.ToVoid(),
                                                             &error));
        if (data.IsNull()) {
            Abort(error);
            return 0;
        }

        assert(data.data >= src);
        assert(data.data <= src_end);
        assert(data.end() <= src_end);

        src = data.data;

        if (!data.IsEmpty()) {
            assert(!parser.HasEnded());

            size_t nbytes;

            if (verbatim) {
                /* postpone this data chunk; try to send it all later in
                   one big block */
                nbytes = data.size;
            } else {
                had_output = true;
                nbytes = InvokeData(src, data.size);
                assert(nbytes <= data.size);

                if (nbytes == 0) {
                    if (closed)
                        return 0;
                    else
                        break;
                }
            }

            src += nbytes;

            bool finished = parser.Consume(nbytes);
            if (!finished && !verbatim)
                break;
        } else if (parser.HasEnded()) {
            break;
        } else {
            assert(src == src_end);
        }
    }

    const size_t position = src - src_begin;
    if (verbatim && position > 0) {
        /* send all chunks in one big block */
        had_output = true;
        size_t nbytes = InvokeData(src_begin, position);
        if (closed)
            return 0;

        /* postpone the rest that was not handled; it will not be
           parsed again */
        pending_verbatim = position - nbytes;
        if (parser.HasEnded()) {
                if (pending_verbatim > 0)
                    /* not everything could be sent; postpone to
                       next call */
                    eof_verbatim = true;
                else if (!EofDetected())
                    return 0;
        }

        return nbytes;
    } else if (parser.HasEnded() && !EofDetected())
        return 0;

    return position;
}

/*
 * istream handler
 *
 */

size_t
DechunkIstream::OnData(const void *data, size_t length)
{
    assert(!verbatim || length >= pending_verbatim);

    if (verbatim && eof_verbatim) {
        /* during the last call, the EOF chunk was parsed, but we
           could not handle it yet, because the handler did not
           consume all data yet; try to send the remaining pre-EOF
           data again and then handle the EOF chunk */

        assert(pending_verbatim > 0);

        assert(length >= pending_verbatim);

        had_output = true;
        size_t nbytes = InvokeData(data, pending_verbatim);
        if (nbytes == 0)
            return 0;

        pending_verbatim -= nbytes;
        if (pending_verbatim > 0)
            /* more data needed */
            return nbytes;

        return EofDetected() ? nbytes : 0;
    }

    const ScopePoolRef ref(GetPool() TRACE_ARGS);
    return Feed(data, length);
}

void
DechunkIstream::OnEof()
{
    assert(!closed);

    input.Clear();

    closed = true;

    if (eof)
        return;

    GError *error =
        g_error_new_literal(dechunk_quark(), 0,
                            "premature EOF in dechunker");
    DestroyError(error);
}

void
DechunkIstream::OnError(GError *error)
{
    input.Clear();

    closed = true;

    if (eof)
        g_error_free(error);
    else
        DestroyError(error);
}

/*
 * istream implementation
 *
 */

off_t
DechunkIstream::_GetAvailable(bool partial)
{
    if (partial)
        return (off_t)parser.GetAvailable();

    return (off_t)-1;
}

void
DechunkIstream::_Read()
{
    const ScopePoolRef ref(GetPool() TRACE_ARGS);

    had_output = false;

    do {
        had_input = false;
        input.Read();
    } while (input.IsDefined() && had_input && !had_output);
}

void
DechunkIstream::_Close()
{
    assert(!eof);
    assert(!closed);

    closed = true;

    input.ClearAndClose();
    Destroy();
}

/*
 * constructor
 *
 */

Istream *
istream_dechunk_new(struct pool *pool, Istream &input,
                    DechunkHandler &dechunk_handler)
{
    return NewIstream<DechunkIstream>(*pool, input, dechunk_handler);
}

bool
istream_dechunk_check_verbatim(Istream &i)
{
    auto *dechunk = DechunkIstream::CheckCast(i);
    if (dechunk == nullptr)
        /* not a DechunkIstream instance */
        return false;

    dechunk->SetVerbatim();
    return true;
}
<|endoftext|>


<commit_before>/*
 * Process CM4all commands in a HTML stream, e.g. embeddings.
 *
 * author: Max Kellermann <mk@cm4all.com>
 */

#include "istream_replace.hxx"
#include "FacadeIstream.hxx"
#include "growing_buffer.hxx"
#include "util/ConstBuffer.hxx"
#include "pool.hxx"

#include <inline/poison.h>
#include <daemon/log.h>

#include <glib.h>

#include <assert.h>

struct ReplaceIstream final : FacadeIstream {
    struct Substitution final : IstreamHandler {
        Substitution *next = nullptr;
        ReplaceIstream &replace;
        const off_t start;
        off_t end;
        IstreamPointer istream;

        Substitution(ReplaceIstream &_replace, off_t _start, off_t _end,
                     Istream *_stream)
            :replace(_replace),
             start(_start), end(_end),
             istream(_stream, *this)
        {
        }

        gcc_pure
        bool IsActive() const;

        /* virtual methods from class IstreamHandler */
        size_t OnData(const void *data, size_t length) override;
        void OnEof() override;
        void OnError(GError *error) override;
    };

    bool finished = false, read_locked = false;
    bool had_input, had_output;

    GrowingBuffer *const buffer;
    off_t source_length = 0, position = 0;

    /**
     * The offset given by istream_replace_settle() or the end offset
     * of the last substitution (whichever is bigger).
     */
    off_t settled_position = 0;

    GrowingBufferReader reader;

    Substitution *first_substitution = nullptr,
        **append_substitution_p = &first_substitution;

#ifndef NDEBUG
    off_t last_substitution_end = 0;
#endif

    ReplaceIstream(struct pool &p, Istream &_input);

    using FacadeIstream::GetPool;
    using FacadeIstream::HasInput;

    void DestroyReplace();

    /**
     * Is the buffer at the end-of-file position?
     */
    bool IsBufferAtEOF() const {
        return position == source_length;
    }

    /**
     * Is the object at end-of-file?
     */
    bool IsEOF() const {
        return !input.IsDefined() && finished &&
            first_substitution == nullptr &&
            IsBufferAtEOF();
    }

    /**
     * Copy the next chunk from the source buffer to the istream
     * handler.
     *
     * @return 0 if the istream handler is not blocking; the number of
     * bytes remaining in the buffer if it is blocking
     */
    size_t TryReadFromBuffer();

    /**
     * Copy data from the source buffer to the istream handler.
     *
     * @return 0 if the istream handler is not blocking; the number of
     * bytes remaining in the buffer if it is blocking
     */
    size_t ReadFromBuffer(size_t max_length);

    size_t ReadFromBufferLoop(off_t end);

    void TryRead();

    void ReadCheckEmpty();

    /**
     * Read data from substitution objects.
     */
    bool ReadSubstitution();

    /**
     * Activate the next substitution object after s.
     */
    void ToNextSubstitution(ReplaceIstream::Substitution *s);

    /* virtual methods from class IstreamHandler */
    size_t OnData(const void *data, size_t length) override;
    void OnEof() override;
    void OnError(GError *error) override;

    /* virtual methods from class Istream */

    off_t _GetAvailable(bool partial) override;
    void _Read() override;
    void _Close() override;
};

static GQuark
replace_quark(void)
{
    return g_quark_from_static_string("replace");
}

/**
 * Is this substitution object is active, i.e. its data is the next
 * being written?
 */
bool
ReplaceIstream::Substitution::IsActive() const
{
    assert(replace.first_substitution != nullptr);
    assert(replace.first_substitution->start <= start);
    assert(start >= replace.position);

    return this == replace.first_substitution && replace.position == start;
}

void
ReplaceIstream::ToNextSubstitution(ReplaceIstream::Substitution *s)
{
    assert(first_substitution == s);
    assert(s->IsActive());
    assert(!s->istream.IsDefined());
    assert(s->start <= s->end);

    reader.Skip(s->end - s->start);
    position = s->end;

    first_substitution = s->next;
    if (first_substitution == nullptr) {
        assert(append_substitution_p == &s->next);
        append_substitution_p = &first_substitution;
    }

    p_free(&GetPool(), s);

    assert(first_substitution == nullptr ||
           first_substitution->start >= position);

    if (IsEOF()) {
        DestroyEof();
        return;
    }

    /* don't recurse if we're being called from ReadSubstitution() */
    if (!read_locked) {
        const ScopePoolRef ref(GetPool() TRACE_ARGS);
        TryRead();
    }
}

/*
 * istream handler
 *
 */

inline size_t
ReplaceIstream::Substitution::OnData(const void *data, size_t length)
{
    if (IsActive()) {
        replace.had_output = true;
        return replace.InvokeData(data, length);
    } else
        return 0;
}

inline void
ReplaceIstream::Substitution::OnEof()
{
    istream.Clear();

    if (IsActive())
        replace.ToNextSubstitution(this);
}

inline void
ReplaceIstream::Substitution::OnError(GError *error)
{
    istream.Clear();

    replace.DestroyReplace();

    if (replace.HasInput())
        replace.ClearAndCloseInput();

    replace.DestroyError(error);
}

/*
 * misc methods
 *
 */

void
ReplaceIstream::DestroyReplace()
{
    assert(source_length != (off_t)-1);

    /* source_length -1 is the "destroyed" marker */
    source_length = (off_t)-1;

    while (first_substitution != nullptr) {
        auto *s = first_substitution;
        first_substitution = s->next;

        if (s->istream.IsDefined())
            s->istream.ClearAndClose();
    }
}

bool
ReplaceIstream::ReadSubstitution()
{
    while (first_substitution != nullptr && first_substitution->IsActive()) {
        auto *s = first_substitution;

        read_locked = true;

        if (s->istream.IsDefined())
            s->istream.Read();
        else
            ToNextSubstitution(s);

        read_locked = false;

        /* we assume the substitution object is blocking if it hasn't
           reached EOF with this one call */
        if (s == first_substitution)
            return true;
    }

    return false;
}

inline size_t
ReplaceIstream::ReadFromBuffer(size_t max_length)
{
    assert(max_length > 0);

    auto src = reader.Read();
    assert(!src.IsNull());
    assert(!src.IsEmpty());

    if (src.size > max_length)
        src.size = max_length;

    had_output = true;
    size_t nbytes = InvokeData(src.data, src.size);
    assert(nbytes <= src.size);

    if (nbytes == 0)
        /* istream_replace has been closed */
        return src.size;

    reader.Consume(nbytes);
    position += nbytes;

    assert(position <= source_length);

    return src.size - nbytes;
}

inline size_t
ReplaceIstream::ReadFromBufferLoop(off_t end)
{
    assert(end > position);
    assert(end <= source_length);

    /* this loop is required to cross the GrowingBuffer borders */
    size_t rest;
    do {
#ifndef NDEBUG
        PoolNotify notify(GetPool());
#endif

        size_t max_length = (size_t)(end - position);
        rest = ReadFromBuffer(max_length);

#ifndef NDEBUG
        if (notify.Denotify()) {
            assert(rest > 0);
            break;
        }
#endif

        assert(position <= end);
    } while (rest == 0 && position < end);

    return rest;
}

size_t
ReplaceIstream::TryReadFromBuffer()
{
    off_t end;
    if (first_substitution == nullptr) {
        if (finished)
            end = source_length;
        else if (position < settled_position)
            end = settled_position;
        else
            /* block after the last substitution, unless the caller
               has already set the "finished" flag */
            return 1;

        assert(position < source_length);
    } else {
        end = first_substitution->start;
        assert(end >= position);

        if (end == position)
            return 0;
    }

    size_t rest = ReadFromBufferLoop(end);
    if (rest == 0 && position == source_length &&
        first_substitution == nullptr &&
        !input.IsDefined())
        DestroyEof();

    return rest;
}

void
ReplaceIstream::TryRead()
{
    assert(position <= source_length);

    /* read until someone (input or output) blocks */
    size_t rest;
    do {
        bool blocking = ReadSubstitution();
        if (blocking || IsBufferAtEOF() || source_length == (off_t)-1)
            break;

        rest = TryReadFromBuffer();
    } while (rest == 0 && first_substitution != nullptr);
}

void
ReplaceIstream::ReadCheckEmpty()
{
    assert(finished);
    assert(!input.IsDefined());

    if (IsEOF())
        DestroyEof();
    else {
        const ScopePoolRef ref(GetPool() TRACE_ARGS);
        TryRead();
    }
}

/*
 * input handler
 *
 */

inline size_t
ReplaceIstream::OnData(const void *data, size_t length)
{
    had_input = true;

    if (source_length >= 8 * 1024 * 1024) {
        ClearAndCloseInput();
        DestroyReplace();

        GError *error =
            g_error_new_literal(replace_quark(), 0,
                                "file too large for processor");
        DestroyError(error);
        return 0;
    }

    growing_buffer_write_buffer(buffer, data, length);
    source_length += (off_t)length;

    reader.Update();

    const ScopePoolRef ref(GetPool() TRACE_ARGS);

    TryReadFromBuffer();
    if (!input.IsDefined())
        /* the istream API mandates that we must return 0 if the
           stream is finished */
        length = 0;

    return length;
}

inline void
ReplaceIstream::OnEof()
{
    input.Clear();

    if (finished)
        ReadCheckEmpty();
}

inline void
ReplaceIstream::OnError(GError *error)
{
    DestroyReplace();
    input.Clear();
    DestroyError(error);
}

/*
 * istream implementation
 *
 */

off_t
ReplaceIstream::_GetAvailable(bool partial)
{
    off_t length, position2 = 0, l;

    if (!partial && !finished)
        /* we don't know yet how many substitutions will come, so we
           cannot calculate the exact rest */
        return (off_t)-1;

    /* get available bytes from input */

    if (HasInput() && finished) {
        length = input.GetAvailable(partial);
        if (length == (off_t)-1) {
            if (!partial)
                return (off_t)-1;
            length = 0;
        }
    } else
        length = 0;

    /* add available bytes from substitutions (and the source buffers
       before the substitutions) */

    position2 = position;

    for (auto subst = first_substitution;
         subst != nullptr; subst = subst->next) {
        assert(position2 <= subst->start);

        length += subst->start - position2;

        if (subst->istream.IsDefined()) {
            l = subst->istream.GetAvailable(partial);
            if (l != (off_t)-1)
                length += l;
            else if (!partial)
                return (off_t)-1;
        }

        position2 = subst->end;
    }

    /* add available bytes from tail (if known yet) */

    if (finished)
        length += source_length - position2;

    return length;
}

void
ReplaceIstream::_Read()
{
    const ScopePoolRef ref(GetPool() TRACE_ARGS);

    TryRead();

    if (!HasInput())
        return;

    had_output = false;

    do {
        had_input = false;
        input.Read();
    } while (had_input && !had_output && HasInput());
}

void
ReplaceIstream::_Close()
{
    DestroyReplace();

    if (HasInput())
        ClearAndCloseInput();

    Destroy();
}

/*
 * constructor
 *
 */

inline ReplaceIstream::ReplaceIstream(struct pool &p, Istream &_input)
    :FacadeIstream(p, _input),
     buffer(growing_buffer_new(&p, 4096)),
     reader(*buffer)
{
}

Istream *
istream_replace_new(struct pool &pool, Istream &input)
{
    return NewIstream<ReplaceIstream>(pool, input);
}

void
istream_replace_add(Istream &istream, off_t start, off_t end,
                    Istream *contents)
{
    auto &replace = (ReplaceIstream &)istream;

    assert(!replace.finished);
    assert(start >= 0);
    assert(start <= end);
    assert(start >= replace.settled_position);
    assert(start >= replace.last_substitution_end);

    if (contents == nullptr && start == end)
        return;

    auto s =
        NewFromPool<ReplaceIstream::Substitution>(replace.GetPool(),
                                                  replace, start, end,
                                                  contents);

    replace.settled_position = end;

#ifndef NDEBUG
    replace.last_substitution_end = end;
#endif

    *replace.append_substitution_p = s;
    replace.append_substitution_p = &s->next;
}

static ReplaceIstream::Substitution *
replace_get_last_substitution(ReplaceIstream &replace)
{
    auto *substitution = replace.first_substitution;
    assert(substitution != nullptr);

    while (substitution->next != nullptr)
        substitution = substitution->next;

    assert(substitution->end <= replace.settled_position);
    assert(substitution->end == replace.last_substitution_end);
    return substitution;
}

void
istream_replace_extend(Istream &istream, gcc_unused off_t start, off_t end)
{
    auto &replace = (ReplaceIstream &)istream;
    assert(!replace.finished);

    auto *substitution = replace_get_last_substitution(replace);
    assert(substitution->start == start);
    assert(substitution->end == replace.settled_position);
    assert(substitution->end == replace.last_substitution_end);
    assert(end >= substitution->end);

    substitution->end = end;
    replace.settled_position = end;
#ifndef NDEBUG
    replace.last_substitution_end = end;
#endif
}

void
istream_replace_settle(Istream &istream, off_t offset)
{
    auto &replace = (ReplaceIstream &)istream;
    assert(!replace.finished);
    assert(offset >= replace.settled_position);

    replace.settled_position = offset;
}

void
istream_replace_finish(Istream &istream)
{
    auto &replace = (ReplaceIstream &)istream;
    assert(!replace.finished);

    replace.finished = true;

    if (!replace.HasInput())
        replace.ReadCheckEmpty();
}
<commit_msg>istream/replace: use class IstreamSink<commit_after>/*
 * Process CM4all commands in a HTML stream, e.g. embeddings.
 *
 * author: Max Kellermann <mk@cm4all.com>
 */

#include "istream_replace.hxx"
#include "FacadeIstream.hxx"
#include "Sink.hxx"
#include "growing_buffer.hxx"
#include "util/ConstBuffer.hxx"
#include "pool.hxx"

#include <inline/poison.h>
#include <daemon/log.h>

#include <glib.h>

#include <assert.h>

struct ReplaceIstream final : FacadeIstream {
    struct Substitution final : IstreamSink {
        Substitution *next = nullptr;
        ReplaceIstream &replace;
        const off_t start;
        off_t end;

        Substitution(ReplaceIstream &_replace, off_t _start, off_t _end,
                     Istream &_input)
            :IstreamSink(_input),
             replace(_replace),
             start(_start), end(_end)
        {
        }

        bool IsDefined() const {
            return input.IsDefined();
        }

        off_t GetAvailable(bool partial) const {
            return input.GetAvailable(partial);
        }

        void Read() {
            input.Read();
        }

        using IstreamSink::ClearAndCloseInput;

        gcc_pure
        bool IsActive() const;

        /* virtual methods from class IstreamHandler */
        size_t OnData(const void *data, size_t length) override;
        void OnEof() override;
        void OnError(GError *error) override;
    };

    bool finished = false, read_locked = false;
    bool had_input, had_output;

    GrowingBuffer *const buffer;
    off_t source_length = 0, position = 0;

    /**
     * The offset given by istream_replace_settle() or the end offset
     * of the last substitution (whichever is bigger).
     */
    off_t settled_position = 0;

    GrowingBufferReader reader;

    Substitution *first_substitution = nullptr,
        **append_substitution_p = &first_substitution;

#ifndef NDEBUG
    off_t last_substitution_end = 0;
#endif

    ReplaceIstream(struct pool &p, Istream &_input);

    using FacadeIstream::GetPool;
    using FacadeIstream::HasInput;

    void DestroyReplace();

    /**
     * Is the buffer at the end-of-file position?
     */
    bool IsBufferAtEOF() const {
        return position == source_length;
    }

    /**
     * Is the object at end-of-file?
     */
    bool IsEOF() const {
        return !input.IsDefined() && finished &&
            first_substitution == nullptr &&
            IsBufferAtEOF();
    }

    /**
     * Copy the next chunk from the source buffer to the istream
     * handler.
     *
     * @return 0 if the istream handler is not blocking; the number of
     * bytes remaining in the buffer if it is blocking
     */
    size_t TryReadFromBuffer();

    /**
     * Copy data from the source buffer to the istream handler.
     *
     * @return 0 if the istream handler is not blocking; the number of
     * bytes remaining in the buffer if it is blocking
     */
    size_t ReadFromBuffer(size_t max_length);

    size_t ReadFromBufferLoop(off_t end);

    void TryRead();

    void ReadCheckEmpty();

    /**
     * Read data from substitution objects.
     */
    bool ReadSubstitution();

    /**
     * Activate the next substitution object after s.
     */
    void ToNextSubstitution(ReplaceIstream::Substitution *s);

    /* virtual methods from class IstreamHandler */
    size_t OnData(const void *data, size_t length) override;
    void OnEof() override;
    void OnError(GError *error) override;

    /* virtual methods from class Istream */

    off_t _GetAvailable(bool partial) override;
    void _Read() override;
    void _Close() override;
};

static GQuark
replace_quark(void)
{
    return g_quark_from_static_string("replace");
}

/**
 * Is this substitution object is active, i.e. its data is the next
 * being written?
 */
bool
ReplaceIstream::Substitution::IsActive() const
{
    assert(replace.first_substitution != nullptr);
    assert(replace.first_substitution->start <= start);
    assert(start >= replace.position);

    return this == replace.first_substitution && replace.position == start;
}

void
ReplaceIstream::ToNextSubstitution(ReplaceIstream::Substitution *s)
{
    assert(first_substitution == s);
    assert(s->IsActive());
    assert(!s->IsDefined());
    assert(s->start <= s->end);

    reader.Skip(s->end - s->start);
    position = s->end;

    first_substitution = s->next;
    if (first_substitution == nullptr) {
        assert(append_substitution_p == &s->next);
        append_substitution_p = &first_substitution;
    }

    p_free(&GetPool(), s);

    assert(first_substitution == nullptr ||
           first_substitution->start >= position);

    if (IsEOF()) {
        DestroyEof();
        return;
    }

    /* don't recurse if we're being called from ReadSubstitution() */
    if (!read_locked) {
        const ScopePoolRef ref(GetPool() TRACE_ARGS);
        TryRead();
    }
}

/*
 * istream handler
 *
 */

inline size_t
ReplaceIstream::Substitution::OnData(const void *data, size_t length)
{
    if (IsActive()) {
        replace.had_output = true;
        return replace.InvokeData(data, length);
    } else
        return 0;
}

inline void
ReplaceIstream::Substitution::OnEof()
{
    input.Clear();

    if (IsActive())
        replace.ToNextSubstitution(this);
}

inline void
ReplaceIstream::Substitution::OnError(GError *error)
{
    ClearInput();

    replace.DestroyReplace();

    if (replace.HasInput())
        replace.ClearAndCloseInput();

    replace.DestroyError(error);
}

/*
 * misc methods
 *
 */

void
ReplaceIstream::DestroyReplace()
{
    assert(source_length != (off_t)-1);

    /* source_length -1 is the "destroyed" marker */
    source_length = (off_t)-1;

    while (first_substitution != nullptr) {
        auto *s = first_substitution;
        first_substitution = s->next;

        if (s->IsDefined())
            s->ClearAndCloseInput();
    }
}

bool
ReplaceIstream::ReadSubstitution()
{
    while (first_substitution != nullptr && first_substitution->IsActive()) {
        auto *s = first_substitution;

        read_locked = true;

        if (s->IsDefined())
            s->Read();
        else
            ToNextSubstitution(s);

        read_locked = false;

        /* we assume the substitution object is blocking if it hasn't
           reached EOF with this one call */
        if (s == first_substitution)
            return true;
    }

    return false;
}

inline size_t
ReplaceIstream::ReadFromBuffer(size_t max_length)
{
    assert(max_length > 0);

    auto src = reader.Read();
    assert(!src.IsNull());
    assert(!src.IsEmpty());

    if (src.size > max_length)
        src.size = max_length;

    had_output = true;
    size_t nbytes = InvokeData(src.data, src.size);
    assert(nbytes <= src.size);

    if (nbytes == 0)
        /* istream_replace has been closed */
        return src.size;

    reader.Consume(nbytes);
    position += nbytes;

    assert(position <= source_length);

    return src.size - nbytes;
}

inline size_t
ReplaceIstream::ReadFromBufferLoop(off_t end)
{
    assert(end > position);
    assert(end <= source_length);

    /* this loop is required to cross the GrowingBuffer borders */
    size_t rest;
    do {
#ifndef NDEBUG
        PoolNotify notify(GetPool());
#endif

        size_t max_length = (size_t)(end - position);
        rest = ReadFromBuffer(max_length);

#ifndef NDEBUG
        if (notify.Denotify()) {
            assert(rest > 0);
            break;
        }
#endif

        assert(position <= end);
    } while (rest == 0 && position < end);

    return rest;
}

size_t
ReplaceIstream::TryReadFromBuffer()
{
    off_t end;
    if (first_substitution == nullptr) {
        if (finished)
            end = source_length;
        else if (position < settled_position)
            end = settled_position;
        else
            /* block after the last substitution, unless the caller
               has already set the "finished" flag */
            return 1;

        assert(position < source_length);
    } else {
        end = first_substitution->start;
        assert(end >= position);

        if (end == position)
            return 0;
    }

    size_t rest = ReadFromBufferLoop(end);
    if (rest == 0 && position == source_length &&
        first_substitution == nullptr &&
        !input.IsDefined())
        DestroyEof();

    return rest;
}

void
ReplaceIstream::TryRead()
{
    assert(position <= source_length);

    /* read until someone (input or output) blocks */
    size_t rest;
    do {
        bool blocking = ReadSubstitution();
        if (blocking || IsBufferAtEOF() || source_length == (off_t)-1)
            break;

        rest = TryReadFromBuffer();
    } while (rest == 0 && first_substitution != nullptr);
}

void
ReplaceIstream::ReadCheckEmpty()
{
    assert(finished);
    assert(!input.IsDefined());

    if (IsEOF())
        DestroyEof();
    else {
        const ScopePoolRef ref(GetPool() TRACE_ARGS);
        TryRead();
    }
}

/*
 * input handler
 *
 */

inline size_t
ReplaceIstream::OnData(const void *data, size_t length)
{
    had_input = true;

    if (source_length >= 8 * 1024 * 1024) {
        ClearAndCloseInput();
        DestroyReplace();

        GError *error =
            g_error_new_literal(replace_quark(), 0,
                                "file too large for processor");
        DestroyError(error);
        return 0;
    }

    growing_buffer_write_buffer(buffer, data, length);
    source_length += (off_t)length;

    reader.Update();

    const ScopePoolRef ref(GetPool() TRACE_ARGS);

    TryReadFromBuffer();
    if (!input.IsDefined())
        /* the istream API mandates that we must return 0 if the
           stream is finished */
        length = 0;

    return length;
}

inline void
ReplaceIstream::OnEof()
{
    input.Clear();

    if (finished)
        ReadCheckEmpty();
}

inline void
ReplaceIstream::OnError(GError *error)
{
    DestroyReplace();
    input.Clear();
    DestroyError(error);
}

/*
 * istream implementation
 *
 */

off_t
ReplaceIstream::_GetAvailable(bool partial)
{
    off_t length, position2 = 0, l;

    if (!partial && !finished)
        /* we don't know yet how many substitutions will come, so we
           cannot calculate the exact rest */
        return (off_t)-1;

    /* get available bytes from input */

    if (HasInput() && finished) {
        length = input.GetAvailable(partial);
        if (length == (off_t)-1) {
            if (!partial)
                return (off_t)-1;
            length = 0;
        }
    } else
        length = 0;

    /* add available bytes from substitutions (and the source buffers
       before the substitutions) */

    position2 = position;

    for (auto subst = first_substitution;
         subst != nullptr; subst = subst->next) {
        assert(position2 <= subst->start);

        length += subst->start - position2;

        if (subst->IsDefined()) {
            l = subst->GetAvailable(partial);
            if (l != (off_t)-1)
                length += l;
            else if (!partial)
                return (off_t)-1;
        }

        position2 = subst->end;
    }

    /* add available bytes from tail (if known yet) */

    if (finished)
        length += source_length - position2;

    return length;
}

void
ReplaceIstream::_Read()
{
    const ScopePoolRef ref(GetPool() TRACE_ARGS);

    TryRead();

    if (!HasInput())
        return;

    had_output = false;

    do {
        had_input = false;
        input.Read();
    } while (had_input && !had_output && HasInput());
}

void
ReplaceIstream::_Close()
{
    DestroyReplace();

    if (HasInput())
        ClearAndCloseInput();

    Destroy();
}

/*
 * constructor
 *
 */

inline ReplaceIstream::ReplaceIstream(struct pool &p, Istream &_input)
    :FacadeIstream(p, _input),
     buffer(growing_buffer_new(&p, 4096)),
     reader(*buffer)
{
}

Istream *
istream_replace_new(struct pool &pool, Istream &input)
{
    return NewIstream<ReplaceIstream>(pool, input);
}

void
istream_replace_add(Istream &istream, off_t start, off_t end,
                    Istream *contents)
{
    auto &replace = (ReplaceIstream &)istream;

    assert(!replace.finished);
    assert(start >= 0);
    assert(start <= end);
    assert(start >= replace.settled_position);
    assert(start >= replace.last_substitution_end);

    if (contents == nullptr && start == end)
        return;

    auto s =
        NewFromPool<ReplaceIstream::Substitution>(replace.GetPool(),
                                                  replace, start, end,
                                                  *contents);

    replace.settled_position = end;

#ifndef NDEBUG
    replace.last_substitution_end = end;
#endif

    *replace.append_substitution_p = s;
    replace.append_substitution_p = &s->next;
}

static ReplaceIstream::Substitution *
replace_get_last_substitution(ReplaceIstream &replace)
{
    auto *substitution = replace.first_substitution;
    assert(substitution != nullptr);

    while (substitution->next != nullptr)
        substitution = substitution->next;

    assert(substitution->end <= replace.settled_position);
    assert(substitution->end == replace.last_substitution_end);
    return substitution;
}

void
istream_replace_extend(Istream &istream, gcc_unused off_t start, off_t end)
{
    auto &replace = (ReplaceIstream &)istream;
    assert(!replace.finished);

    auto *substitution = replace_get_last_substitution(replace);
    assert(substitution->start == start);
    assert(substitution->end == replace.settled_position);
    assert(substitution->end == replace.last_substitution_end);
    assert(end >= substitution->end);

    substitution->end = end;
    replace.settled_position = end;
#ifndef NDEBUG
    replace.last_substitution_end = end;
#endif
}

void
istream_replace_settle(Istream &istream, off_t offset)
{
    auto &replace = (ReplaceIstream &)istream;
    assert(!replace.finished);
    assert(offset >= replace.settled_position);

    replace.settled_position = offset;
}

void
istream_replace_finish(Istream &istream)
{
    auto &replace = (ReplaceIstream &)istream;
    assert(!replace.finished);

    replace.finished = true;

    if (!replace.HasInput())
        replace.ReadCheckEmpty();
}
<|endoftext|>


<commit_before>/* vim: ts=2:sw=2:expandtab
 *
 * VHDE - The VHDL Diagram Editor
 * Copyright (C) 2009-2010  Maurice van der Pot <griffon26@kfk4ever.com>
 *
 * 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 <stdio.h>

#include "i_named_item.h"
#include "layout_component.h"
#include "layout_port.h"

/*
 * Public methods
 */

LayoutComponent::LayoutComponent():
  m_pVHDLEntity(nullptr)
{
  m_position.x = 800;
  m_position.y = 50;
}

void LayoutComponent::associateEntity(INamedItem *pVHDLEntity)
{
  g_assert(pVHDLEntity);
  printf("LayoutComponent(%p)::associateEntity(%p)\n", this, pVHDLEntity);
  m_pVHDLEntity = pVHDLEntity;
}

INamedItem *LayoutComponent::getAssociatedVHDLEntity()
{
  return m_pVHDLEntity;
}

const Glib::ustring &LayoutComponent::getName()
{
  g_assert(m_pVHDLEntity);
  return m_pVHDLEntity->getName();
}

LayoutPort *LayoutComponent::createPort(Edge edge, int position, INamedItem *pVHDLPort)
{
  printf("LayoutComponent::createPort\n");
  auto pLayoutPort = std::make_unique<LayoutPort>();
  auto pRawLayoutPort = pLayoutPort.get();
  pLayoutPort->associateVHDLPort(pVHDLPort);
  addPort(edge, position, std::move(pLayoutPort));
  return pRawLayoutPort;
}

void LayoutComponent::destroyPort(Edge edge, int position)
{
  LayoutPort *pLayoutPort = getPort(edge, position);
  printf("LayoutComponent(%p)::destroyPort(%p)\n", this, pLayoutPort);
  g_assert(pLayoutPort != NULL);
  removePort(pLayoutPort);
}

/*
component {
  size 200 300
  ports {
    LEFT 0 "dinges"
    LEFT 1 "blaat"
  }
}

*/
void LayoutComponent::write(std::ostream &stream, int indent)
{
  std::map<int, LayoutPort *>::iterator it;
  Glib::ustring indentString(indent, ' ');

  stream << indentString << "component {\n"
         << indentString << "  size " << m_size.width << " " << m_size.height << "\n"
         << indentString << "  ports {\n";

  for(auto pPort: m_portOrder)
  {
    auto edgeAndPosition = pPort->getLocation();
    stream << indentString << "    \"" << pPort->getAssociatedVHDLPort()->getName() << "\" " << EDGE_TO_NAME(edgeAndPosition.first) << " " << edgeAndPosition.second << "\n";
  }

  stream << indentString << "  }\n"
         << indentString << "}\n"
         << "\n";
}

<commit_msg>Change default position of entity on stage<commit_after>/* vim: ts=2:sw=2:expandtab
 *
 * VHDE - The VHDL Diagram Editor
 * Copyright (C) 2009-2010  Maurice van der Pot <griffon26@kfk4ever.com>
 *
 * 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 <stdio.h>

#include "i_named_item.h"
#include "layout_component.h"
#include "layout_port.h"

/*
 * Public methods
 */

LayoutComponent::LayoutComponent():
  m_pVHDLEntity(nullptr)
{
  m_position.x = 400;
  m_position.y = 50;
}

void LayoutComponent::associateEntity(INamedItem *pVHDLEntity)
{
  g_assert(pVHDLEntity);
  printf("LayoutComponent(%p)::associateEntity(%p)\n", this, pVHDLEntity);
  m_pVHDLEntity = pVHDLEntity;
}

INamedItem *LayoutComponent::getAssociatedVHDLEntity()
{
  return m_pVHDLEntity;
}

const Glib::ustring &LayoutComponent::getName()
{
  g_assert(m_pVHDLEntity);
  return m_pVHDLEntity->getName();
}

LayoutPort *LayoutComponent::createPort(Edge edge, int position, INamedItem *pVHDLPort)
{
  printf("LayoutComponent::createPort\n");
  auto pLayoutPort = std::make_unique<LayoutPort>();
  auto pRawLayoutPort = pLayoutPort.get();
  pLayoutPort->associateVHDLPort(pVHDLPort);
  addPort(edge, position, std::move(pLayoutPort));
  return pRawLayoutPort;
}

void LayoutComponent::destroyPort(Edge edge, int position)
{
  LayoutPort *pLayoutPort = getPort(edge, position);
  printf("LayoutComponent(%p)::destroyPort(%p)\n", this, pLayoutPort);
  g_assert(pLayoutPort != NULL);
  removePort(pLayoutPort);
}

/*
component {
  size 200 300
  ports {
    LEFT 0 "dinges"
    LEFT 1 "blaat"
  }
}

*/
void LayoutComponent::write(std::ostream &stream, int indent)
{
  std::map<int, LayoutPort *>::iterator it;
  Glib::ustring indentString(indent, ' ');

  stream << indentString << "component {\n"
         << indentString << "  size " << m_size.width << " " << m_size.height << "\n"
         << indentString << "  ports {\n";

  for(auto pPort: m_portOrder)
  {
    auto edgeAndPosition = pPort->getLocation();
    stream << indentString << "    \"" << pPort->getAssociatedVHDLPort()->getName() << "\" " << EDGE_TO_NAME(edgeAndPosition.first) << " " << edgeAndPosition.second << "\n";
  }

  stream << indentString << "  }\n"
         << indentString << "}\n"
         << "\n";
}

<|endoftext|>