<commit_before>/* IBM_PROLOG_BEGIN_TAG                                                   */
/* This is an automatically generated prolog.                             */
/*                                                                        */
/* $Source: src/import/chips/p9/common/scominfo/p9_cu.H $                 */
/*                                                                        */
/* OpenPOWER HostBoot Project                                             */
/*                                                                        */
/* Contributors Listed Below - COPYRIGHT 2015,2019                        */
/* [+] International Business Machines Corp.                              */
/*                                                                        */
/*                                                                        */
/* Licensed under the Apache License, Version 2.0 (the "License");        */
/* you may not use this file except in compliance with the License.       */
/* You may obtain a copy of the License at                                */
/*                                                                        */
/*     http://www.apache.org/licenses/LICENSE-2.0                         */
/*                                                                        */
/* Unless required by applicable law or agreed to in writing, software    */
/* distributed under the License is distributed on an "AS IS" BASIS,      */
/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or        */
/* implied. See the License for the specific language governing           */
/* permissions and limitations under the License.                         */
/*                                                                        */
/* IBM_PROLOG_END_TAG                                                     */
///
/// @file p9_cu.H
/// @brief P9 chip unit definitions
///
/// HWP HWP Owner: jmcgill@us.ibm.com
/// HWP FW Owner: dcrowell@us.ibm.com
/// HWP Team: Infrastructure
/// HWP Level: 1
/// HWP Consumed by: FSP/HB
///

#ifndef P9_CU_H
#define P9_CU_H

// includes
#include <stdint.h>

extern "C"
{
    /// P9 chip unit type enumeration
    typedef enum
    {
        P9C_CHIP,            ///< Cumulus chip (included for future expansion)
        P9N_CHIP,            ///< Nimbus chip (included for future expansion)
        PU_C_CHIPUNIT,       ///< Core
        PU_EQ_CHIPUNIT,      ///< Quad
        PU_EX_CHIPUNIT,      ///< EX
        PU_XBUS_CHIPUNIT,    ///< XBUS
        PU_OBUS_CHIPUNIT,    ///< OBUS
        PU_NV_CHIPUNIT,      ///< NV Link Brick
        PU_PEC_CHIPUNIT,     ///< PCIe (PEC)
        PU_PHB_CHIPUNIT,     ///< PCIe (PHB)
        PU_MI_CHIPUNIT,      ///< MI (Cumulus only)
        PU_DMI_CHIPUNIT,     ///< DMI (Cumulus only)
        PU_MCS_CHIPUNIT,     ///< MCS (Nimbus only)
        PU_MCA_CHIPUNIT,     ///< MCA (Nimbus only)
        PU_MCBIST_CHIPUNIT,  ///< MCBIST (Nimbus only)
        PU_OCC_CHIPUNIT,     ///< OCC
        PU_PERV_CHIPUNIT,    ///< Pervasive
        PU_PPE_CHIPUNIT,     ///< PPE
        PU_SBE_CHIPUNIT,     ///< SBE
        PU_CAPP_CHIPUNIT,    ///< CAPP
        NONE,                 ///< None/Invalid
        PU_NVBUS_CHIPUNIT =  PU_NV_CHIPUNIT  ///< DO NOT USE!  TEMPORARY FOR CI ONLY
    } p9ChipUnits_t;

    /// P9 chip unit pairing struct
    struct p9_chipUnitPairing_t
    {
        /// @brief Default constructor
        p9_chipUnitPairing_t()
            : chipUnitType(NONE), chipUnitNum(0) {}
        /// @brief Construct from type/instance number
        p9_chipUnitPairing_t (p9ChipUnits_t type, uint32_t num)
            : chipUnitType(type), chipUnitNum(num) {}

        p9ChipUnits_t chipUnitType;  ///< chip unit type
        uint32_t chipUnitNum;        ///< chip unit instance number
    };

} // extern "C"

#endif /* P9_CU_H */
<commit_msg>Adding SCOM addr translation for PPE chip units<commit_after>/* IBM_PROLOG_BEGIN_TAG                                                   */
/* This is an automatically generated prolog.                             */
/*                                                                        */
/* $Source: src/import/chips/p9/common/scominfo/p9_cu.H $                 */
/*                                                                        */
/* OpenPOWER HostBoot Project                                             */
/*                                                                        */
/* Contributors Listed Below - COPYRIGHT 2015,2019                        */
/* [+] International Business Machines Corp.                              */
/*                                                                        */
/*                                                                        */
/* Licensed under the Apache License, Version 2.0 (the "License");        */
/* you may not use this file except in compliance with the License.       */
/* You may obtain a copy of the License at                                */
/*                                                                        */
/*     http://www.apache.org/licenses/LICENSE-2.0                         */
/*                                                                        */
/* Unless required by applicable law or agreed to in writing, software    */
/* distributed under the License is distributed on an "AS IS" BASIS,      */
/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or        */
/* implied. See the License for the specific language governing           */
/* permissions and limitations under the License.                         */
/*                                                                        */
/* IBM_PROLOG_END_TAG                                                     */
///
/// @file p9_cu.H
/// @brief P9 chip unit definitions
///
/// HWP HWP Owner: jmcgill@us.ibm.com
/// HWP FW Owner: dcrowell@us.ibm.com
/// HWP Team: Infrastructure
/// HWP Level: 2
/// HWP Consumed by: FSP/HB
///

#ifndef P9_CU_H
#define P9_CU_H

// includes
#include <stdint.h>

extern "C"
{
    /// P9 chip unit type enumeration
    typedef enum
    {
        P9C_CHIP,            ///< Cumulus chip (included for future expansion)
        P9N_CHIP,            ///< Nimbus chip (included for future expansion)
        PU_C_CHIPUNIT,       ///< Core
        PU_EQ_CHIPUNIT,      ///< Quad
        PU_EX_CHIPUNIT,      ///< EX
        PU_XBUS_CHIPUNIT,    ///< XBUS
        PU_OBUS_CHIPUNIT,    ///< OBUS
        PU_NV_CHIPUNIT,      ///< NV Link Brick
        PU_PEC_CHIPUNIT,     ///< PCIe (PEC)
        PU_PHB_CHIPUNIT,     ///< PCIe (PHB)
        PU_MI_CHIPUNIT,      ///< MI (Cumulus only)
        PU_DMI_CHIPUNIT,     ///< DMI (Cumulus only)
        PU_MCS_CHIPUNIT,     ///< MCS (Nimbus only)
        PU_MCA_CHIPUNIT,     ///< MCA (Nimbus only)
        PU_MCBIST_CHIPUNIT,  ///< MCBIST (Nimbus only)
        PU_PERV_CHIPUNIT,    ///< Pervasive
        PU_PPE_CHIPUNIT,     ///< PPE
        PU_SBE_CHIPUNIT,     ///< SBE
        PU_CAPP_CHIPUNIT,    ///< CAPP
        NONE,                ///< None/Invalid
    } p9ChipUnits_t;

    /// P9 chip unit pairing struct
    struct p9_chipUnitPairing_t
    {
        /// @brief Default constructor
        p9_chipUnitPairing_t()
            : chipUnitType(NONE), chipUnitNum(0) {}
        /// @brief Construct from type/instance number
        p9_chipUnitPairing_t (p9ChipUnits_t type, uint32_t num)
            : chipUnitType(type), chipUnitNum(num) {}

        p9ChipUnits_t chipUnitType;  ///< chip unit type
        uint32_t chipUnitNum;        ///< chip unit instance number
    };

    /// P9 PPE Chip Unit Instance Number enumeration
    /// PPE name        Nimbus    Cumulus
    /// SBE             0         0
    /// GPE0..3         10..13    10..13
    /// CME0            20..25    20..25
    /// CME1            30..35    30..35
    /// IO PPE (xbus)   40        40
    /// IO PPE (obus)   41,42     41,42
    /// IO PPE (dmi)    NA        43,44
    /// Powerbus PPEs   50        50..52
    typedef enum
    {
        PPE_SBE_CHIPUNIT_NUM         =  0,
        PPE_GPE0_CHIPUNIT_NUM        = 10,
        PPE_GPE3_CHIPUNIT_NUM        = 13,
        PPE_EQ0_CME0_CHIPUNIT_NUM    = 20,   // Quad0-CME0
        PPE_EQ5_CME0_CHIPUNIT_NUM    = 25,   // Quad5-CME0
        PPE_EQ0_CME1_CHIPUNIT_NUM    = 30,   // Quad0-CME1
        PPE_EQ5_CME1_CHIPUNIT_NUM    = 35,   // Quad5-CME1
        PPE_IO_XBUS_CHIPUNIT_NUM     = 40,
        PPE_IO_OB0_CHIPUNIT_NUM      = 41,
        PPE_IO_OB1_CHIPUNIT_NUM      = 42,
        PPE_IO1_DMI_CHIPUNIT_NUM     = 44,
        PPE_PB0_CHIPUNIT_NUM         = 50,
        PPE_PB2_CHIPUNIT_NUM         = 52,
        PPE_LAST_CHIPUNIT_NUM        = PPE_PB2_CHIPUNIT_NUM,
    } p9_ppe_chip_unit_instance_num_t;

} // extern "C"

#endif /* P9_CU_H */
<|endoftext|>


<commit_before><commit_msg>Support profile_file_prefix in python binding (#5864)<commit_after><|endoftext|>


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

#include "SkImageDecoder.h"
#include "SkImage_Base.h"
#include "SkBitmap.h"
#include "SkCanvas.h"
#include "SkData.h"

class SkImage_Codec : public SkImage_Base {
public:
    static SkImage* NewEmpty();

    SkImage_Codec(SkData* encodedData, int width, int height);
    virtual ~SkImage_Codec();

    virtual void onDraw(SkCanvas*, SkScalar, SkScalar, const SkPaint*) const SK_OVERRIDE;
    virtual void onDrawRectToRect(SkCanvas*, const SkRect*, const SkRect&,
                                  const SkPaint*) const SK_OVERRIDE;

    virtual bool isOpaque() const SK_OVERRIDE;

private:
    SkData*     fEncodedData;
    SkBitmap    fBitmap;

    typedef SkImage_Base INHERITED;
};

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

SkImage_Codec::SkImage_Codec(SkData* data, int width, int height) : INHERITED(width, height) {
    fEncodedData = data;
    fEncodedData->ref();
}

SkImage_Codec::~SkImage_Codec() {
    fEncodedData->unref();
}

void SkImage_Codec::onDraw(SkCanvas* canvas, SkScalar x, SkScalar y, const SkPaint* paint) const {
    if (!fBitmap.pixelRef()) {
        // todo: this needs to be thread-safe
        SkBitmap* bitmap = const_cast<SkBitmap*>(&fBitmap);
        if (!SkImageDecoder::DecodeMemory(fEncodedData->bytes(), fEncodedData->size(), bitmap)) {
            return;
        }
    }
    canvas->drawBitmap(fBitmap, x, y, paint);
}

void SkImage_Codec::onDrawRectToRect(SkCanvas* canvas, const SkRect* src, const SkRect& dst,
                                     const SkPaint* paint) const {
    if (!fBitmap.pixelRef()) {
        // todo: this needs to be thread-safe
        SkBitmap* bitmap = const_cast<SkBitmap*>(&fBitmap);
        if (!SkImageDecoder::DecodeMemory(fEncodedData->bytes(), fEncodedData->size(), bitmap)) {
            return;
        }
    }
    canvas->drawBitmapRectToRect(fBitmap, src, dst, paint);
}

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

SkImage* SkImage::NewEncodedData(SkData* data) {
    if (NULL == data) {
        return NULL;
    }

    SkBitmap bitmap;
    if (!SkImageDecoder::DecodeMemory(data->bytes(), data->size(), &bitmap, kUnknown_SkColorType,
                                      SkImageDecoder::kDecodeBounds_Mode)) {
        return NULL;
    }

    return SkNEW_ARGS(SkImage_Codec, (data, bitmap.width(), bitmap.height()));
}

bool SkImage_Codec::isOpaque() const {
    return fBitmap.isOpaque();
}
<commit_msg>Small refactoring in SkImage_Codec<commit_after>/*
 * Copyright 2012 Google Inc.
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

#include "SkImageDecoder.h"
#include "SkImage_Base.h"
#include "SkBitmap.h"
#include "SkCanvas.h"
#include "SkData.h"

class SkImage_Codec : public SkImage_Base {
public:
    static SkImage* NewEmpty();

    SkImage_Codec(SkData* encodedData, int width, int height);
    virtual ~SkImage_Codec();

    virtual void onDraw(SkCanvas*, SkScalar, SkScalar, const SkPaint*) const SK_OVERRIDE;
    virtual void onDrawRectToRect(SkCanvas*, const SkRect*, const SkRect&,
                                  const SkPaint*) const SK_OVERRIDE;

    virtual bool isOpaque() const SK_OVERRIDE;

private:
    bool ensureBitmapDecoded() const;

    SkData*     fEncodedData;
    SkBitmap    fBitmap;

    typedef SkImage_Base INHERITED;
};

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

SkImage_Codec::SkImage_Codec(SkData* data, int width, int height) : INHERITED(width, height) {
    fEncodedData = data;
    fEncodedData->ref();
}

SkImage_Codec::~SkImage_Codec() {
    fEncodedData->unref();
}

bool SkImage_Codec::ensureBitmapDecoded() const {
    if (!fBitmap.pixelRef()) {
        // todo: this needs to be thread-safe
        SkBitmap* bitmap = const_cast<SkBitmap*>(&fBitmap);
        if (!SkImageDecoder::DecodeMemory(fEncodedData->bytes(), fEncodedData->size(), bitmap)) {
            return false;
        }
    }
    return true;
}

void SkImage_Codec::onDraw(SkCanvas* canvas, SkScalar x, SkScalar y, const SkPaint* paint) const {
    if(!this->ensureBitmapDecoded()) {
        return;
    }

    canvas->drawBitmap(fBitmap, x, y, paint);
}

void SkImage_Codec::onDrawRectToRect(SkCanvas* canvas, const SkRect* src, const SkRect& dst,
                                     const SkPaint* paint) const {
    if(!this->ensureBitmapDecoded()) {
        return;
    }

    canvas->drawBitmapRectToRect(fBitmap, src, dst, paint);
}

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

SkImage* SkImage::NewEncodedData(SkData* data) {
    if (NULL == data) {
        return NULL;
    }

    SkBitmap bitmap;
    if (!SkImageDecoder::DecodeMemory(data->bytes(), data->size(), &bitmap, kUnknown_SkColorType,
                                      SkImageDecoder::kDecodeBounds_Mode)) {
        return NULL;
    }

    return SkNEW_ARGS(SkImage_Codec, (data, bitmap.width(), bitmap.height()));
}

bool SkImage_Codec::isOpaque() const {
    return fBitmap.isOpaque();
}
<|endoftext|>


<commit_before>/*
 * Copyright 2007-2020 CM4all GmbH
 * All rights reserved.
 *
 * author: Max Kellermann <mk@cm4all.com>
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * - Redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer.
 *
 * - Redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the
 * distribution.
 *
 * 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
 * FOUNDATION 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 "FileIstream.hxx"
#include "istream.hxx"
#include "New.hxx"
#include "Result.hxx"
#include "io/Buffered.hxx"
#include "io/Open.hxx"
#include "io/UniqueFileDescriptor.hxx"
#include "pool/pool.hxx"
#include "fb_pool.hxx"
#include "SliceFifoBuffer.hxx"
#include "system/Error.hxx"
#include "event/TimerEvent.hxx"
#include "util/RuntimeError.hxx"

#include <assert.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <limits.h>

/**
 * If EAGAIN occurs (on NFS), we try again after 100ms.  We can't
 * check SocketEvent::READ, because the kernel always indicates VFS files as
 * "readable without blocking".
 */
static constexpr Event::Duration file_retry_timeout = std::chrono::milliseconds(100);

class FileIstream final : public Istream {
	FileDescriptor fd;

	FdType fd_type;

	/**
	 * A timer to retry reading after EAGAIN.
	 */
	TimerEvent retry_event;

	off_t rest;
	SliceFifoBuffer buffer;
	const char *path;

public:
	FileIstream(struct pool &p, EventLoop &event_loop,
		    UniqueFileDescriptor &&_fd, FdType _fd_type, off_t _length,
		    const char *_path) noexcept
		:Istream(p),
		 fd(_fd.Steal()), fd_type(_fd_type),
		 retry_event(event_loop, BIND_THIS_METHOD(EventCallback)),
		 rest(_length),
		 path(_path) {}

	~FileIstream() noexcept {
		retry_event.Cancel();
	}

private:
	void CloseHandle() noexcept {
		if (!fd.IsDefined())
			return;

		retry_event.Cancel();

		fd.Close();

		buffer.FreeIfDefined();
	}

	void Abort(std::exception_ptr ep) noexcept {
		CloseHandle();
		DestroyError(ep);
	}

	/**
	 * @return the number of bytes still in the buffer
	 */
	size_t SubmitBuffer() noexcept {
		return ConsumeFromBuffer(buffer);
	}

	void EofDetected() noexcept {
		assert(fd.IsDefined());

		CloseHandle();
		DestroyEof();
	}

	gcc_pure
	size_t GetMaxRead() const noexcept {
		if (rest != (off_t)-1 && rest < (off_t)INT_MAX)
			return (size_t)rest;
		else
			return INT_MAX;
	}

	void TryData() noexcept;
	void TryDirect() noexcept;

	void TryRead() noexcept {
		if (CheckDirect(fd_type))
			TryDirect();
		else
			TryData();
	}

	void EventCallback() noexcept {
		TryRead();
	}

	/* virtual methods from class Istream */

	off_t _GetAvailable(bool partial) noexcept override;
	off_t _Skip(gcc_unused off_t length) noexcept override;

	void _Read() noexcept override {
		retry_event.Cancel();
		TryRead();
	}

	int _AsFd() noexcept override;
	void _Close() noexcept override {
		CloseHandle();
		Destroy();
	}
};

inline void
FileIstream::TryData() noexcept
{
	size_t buffer_rest = 0;

	if (buffer.IsNull()) {
		if (rest != 0)
			buffer.Allocate(fb_pool_get());
	} else {
		const size_t available = buffer.GetAvailable();
		if (available > 0) {
			buffer_rest = SubmitBuffer();
			if (buffer_rest == available)
				/* not a single byte was consumed: we may have been
				   closed, and we must bail out now */
				return;
		}
	}

	if (rest == 0) {
		if (buffer_rest == 0)
			EofDetected();
		return;
	}

	ssize_t nbytes = read_to_buffer(fd.Get(), buffer, GetMaxRead());
	if (nbytes == 0) {
		if (rest == (off_t)-1) {
			rest = 0;
			if (buffer_rest == 0)
				EofDetected();
		} else {
			Abort(std::make_exception_ptr(FormatRuntimeError("premature end of file in '%s'",
									 path)));
		}
		return;
	} else if (nbytes == -1) {
		Abort(std::make_exception_ptr(FormatErrno("Failed to read from '%s'",
							  path)));
		return;
	} else if (nbytes > 0 && rest != (off_t)-1) {
		rest -= (off_t)nbytes;
		assert(rest >= 0);
	}

	assert(!buffer.empty());

	buffer_rest = SubmitBuffer();
	if (buffer_rest == 0 && rest == 0)
		EofDetected();
}

inline void
FileIstream::TryDirect() noexcept
{
	/* first consume the rest of the buffer */
	if (SubmitBuffer() > 0)
		return;

	if (rest == 0) {
		EofDetected();
		return;
	}

	ssize_t nbytes = InvokeDirect(fd_type, fd.Get(), GetMaxRead());
	if (nbytes == ISTREAM_RESULT_CLOSED)
		/* this stream was closed during the direct() callback */
		return;

	if (nbytes > 0 || nbytes == ISTREAM_RESULT_BLOCKING) {
		/* -2 means the callback wasn't able to consume any data right
		   now */
		if (nbytes > 0 && rest != (off_t)-1) {
			rest -= (off_t)nbytes;
			assert(rest >= 0);
			if (rest == 0)
				EofDetected();
		}
	} else if (nbytes == ISTREAM_RESULT_EOF) {
		if (rest == (off_t)-1) {
			EofDetected();
		} else {
			Abort(std::make_exception_ptr(FormatRuntimeError("premature end of file in '%s'",
									 path)));
		}
	} else if (errno == EAGAIN) {
		/* this should only happen for splice(SPLICE_F_NONBLOCK) from
		   NFS files - unfortunately we cannot use SocketEvent::READ
		   here, so we just install a timer which retries after
		   100ms */

		retry_event.Schedule(file_retry_timeout);
	} else {
		/* XXX */
		Abort(std::make_exception_ptr(FormatErrno("Failed to read from '%s'",
							  path)));
	}
}

/*
 * istream implementation
 *
 */

off_t
FileIstream::_GetAvailable(bool partial) noexcept
{
	off_t available;
	if (rest != (off_t)-1)
		available = rest;
	else if (!partial)
		return (off_t)-1;
	else
		available = 0;

	available += buffer.GetAvailable();
	return available;
}

off_t
FileIstream::_Skip(off_t length) noexcept
{
	retry_event.Cancel();

	if (rest == (off_t)-1)
		return (off_t)-1;

	if (length == 0)
		return 0;

	const size_t buffer_available = buffer.GetAvailable();
	if (length < off_t(buffer_available)) {
		buffer.Consume(length);
		Consumed(length);
		return length;
	}

	length -= buffer_available;
	buffer.Clear();

	if (length >= rest) {
		/* skip beyond EOF */

		length = rest;
		rest = 0;
	} else {
		/* seek the file descriptor */

		if (fd.Skip(length) < 0)
			return -1;
		rest -= length;
	}

	off_t result = buffer_available + length;
	Consumed(result);
	return result;
}

int
FileIstream::_AsFd() noexcept
{
	int result_fd = fd.Steal();

	Destroy();

	return result_fd;
}

/*
 * constructor and public methods
 *
 */

UnusedIstreamPtr
istream_file_fd_new(EventLoop &event_loop, struct pool &pool,
		    const char *path,
		    UniqueFileDescriptor fd, FdType fd_type, off_t length) noexcept
{
	assert(fd.IsDefined());
	assert(length >= -1);

	return NewIstreamPtr<FileIstream>(pool, event_loop,
					  std::move(fd), fd_type,
					  length, path);
}

UnusedIstreamPtr
istream_file_new(EventLoop &event_loop, struct pool &pool,
		 const char *path)
{
	auto fd = OpenReadOnly(path);

	struct stat st;
	if (fstat(fd.Get(), &st) < 0)
		throw FormatErrno("Failed to stat %s", path);

	off_t size = S_ISREG(st.st_mode) ? st.st_size : -1;

	FdType fd_type = FdType::FD_FILE;
	if (S_ISCHR(st.st_mode))
		fd_type = FdType::FD_CHARDEV;
	else if (S_ISFIFO(st.st_mode))
		fd_type = FdType::FD_PIPE;
	else if (S_ISSOCK(st.st_mode))
		fd_type = FdType::FD_SOCKET;

	return istream_file_fd_new(event_loop, pool, path,
				   std::move(fd), fd_type, size);
}
<commit_msg>istream/file: fold CloseHandle() into destructor<commit_after>/*
 * Copyright 2007-2020 CM4all GmbH
 * All rights reserved.
 *
 * author: Max Kellermann <mk@cm4all.com>
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * - Redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer.
 *
 * - Redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the
 * distribution.
 *
 * 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
 * FOUNDATION 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 "FileIstream.hxx"
#include "istream.hxx"
#include "New.hxx"
#include "Result.hxx"
#include "io/Buffered.hxx"
#include "io/Open.hxx"
#include "io/UniqueFileDescriptor.hxx"
#include "pool/pool.hxx"
#include "fb_pool.hxx"
#include "SliceFifoBuffer.hxx"
#include "system/Error.hxx"
#include "event/TimerEvent.hxx"
#include "util/RuntimeError.hxx"

#include <assert.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <limits.h>

/**
 * If EAGAIN occurs (on NFS), we try again after 100ms.  We can't
 * check SocketEvent::READ, because the kernel always indicates VFS files as
 * "readable without blocking".
 */
static constexpr Event::Duration file_retry_timeout = std::chrono::milliseconds(100);

class FileIstream final : public Istream {
	FileDescriptor fd;

	FdType fd_type;

	/**
	 * A timer to retry reading after EAGAIN.
	 */
	TimerEvent retry_event;

	off_t rest;
	SliceFifoBuffer buffer;
	const char *path;

public:
	FileIstream(struct pool &p, EventLoop &event_loop,
		    UniqueFileDescriptor &&_fd, FdType _fd_type, off_t _length,
		    const char *_path) noexcept
		:Istream(p),
		 fd(_fd.Steal()), fd_type(_fd_type),
		 retry_event(event_loop, BIND_THIS_METHOD(EventCallback)),
		 rest(_length),
		 path(_path) {}

	~FileIstream() noexcept {
		retry_event.Cancel();

		if (fd.IsDefined())
			fd.Close();

		buffer.FreeIfDefined();
	}

private:
	void Abort(std::exception_ptr ep) noexcept {
		DestroyError(ep);
	}

	/**
	 * @return the number of bytes still in the buffer
	 */
	size_t SubmitBuffer() noexcept {
		return ConsumeFromBuffer(buffer);
	}

	void EofDetected() noexcept {
		assert(fd.IsDefined());

		DestroyEof();
	}

	gcc_pure
	size_t GetMaxRead() const noexcept {
		if (rest != (off_t)-1 && rest < (off_t)INT_MAX)
			return (size_t)rest;
		else
			return INT_MAX;
	}

	void TryData() noexcept;
	void TryDirect() noexcept;

	void TryRead() noexcept {
		if (CheckDirect(fd_type))
			TryDirect();
		else
			TryData();
	}

	void EventCallback() noexcept {
		TryRead();
	}

	/* virtual methods from class Istream */

	off_t _GetAvailable(bool partial) noexcept override;
	off_t _Skip(gcc_unused off_t length) noexcept override;

	void _Read() noexcept override {
		retry_event.Cancel();
		TryRead();
	}

	int _AsFd() noexcept override;
	void _Close() noexcept override {
		Destroy();
	}
};

inline void
FileIstream::TryData() noexcept
{
	size_t buffer_rest = 0;

	if (buffer.IsNull()) {
		if (rest != 0)
			buffer.Allocate(fb_pool_get());
	} else {
		const size_t available = buffer.GetAvailable();
		if (available > 0) {
			buffer_rest = SubmitBuffer();
			if (buffer_rest == available)
				/* not a single byte was consumed: we may have been
				   closed, and we must bail out now */
				return;
		}
	}

	if (rest == 0) {
		if (buffer_rest == 0)
			EofDetected();
		return;
	}

	ssize_t nbytes = read_to_buffer(fd.Get(), buffer, GetMaxRead());
	if (nbytes == 0) {
		if (rest == (off_t)-1) {
			rest = 0;
			if (buffer_rest == 0)
				EofDetected();
		} else {
			Abort(std::make_exception_ptr(FormatRuntimeError("premature end of file in '%s'",
									 path)));
		}
		return;
	} else if (nbytes == -1) {
		Abort(std::make_exception_ptr(FormatErrno("Failed to read from '%s'",
							  path)));
		return;
	} else if (nbytes > 0 && rest != (off_t)-1) {
		rest -= (off_t)nbytes;
		assert(rest >= 0);
	}

	assert(!buffer.empty());

	buffer_rest = SubmitBuffer();
	if (buffer_rest == 0 && rest == 0)
		EofDetected();
}

inline void
FileIstream::TryDirect() noexcept
{
	/* first consume the rest of the buffer */
	if (SubmitBuffer() > 0)
		return;

	if (rest == 0) {
		EofDetected();
		return;
	}

	ssize_t nbytes = InvokeDirect(fd_type, fd.Get(), GetMaxRead());
	if (nbytes == ISTREAM_RESULT_CLOSED)
		/* this stream was closed during the direct() callback */
		return;

	if (nbytes > 0 || nbytes == ISTREAM_RESULT_BLOCKING) {
		/* -2 means the callback wasn't able to consume any data right
		   now */
		if (nbytes > 0 && rest != (off_t)-1) {
			rest -= (off_t)nbytes;
			assert(rest >= 0);
			if (rest == 0)
				EofDetected();
		}
	} else if (nbytes == ISTREAM_RESULT_EOF) {
		if (rest == (off_t)-1) {
			EofDetected();
		} else {
			Abort(std::make_exception_ptr(FormatRuntimeError("premature end of file in '%s'",
									 path)));
		}
	} else if (errno == EAGAIN) {
		/* this should only happen for splice(SPLICE_F_NONBLOCK) from
		   NFS files - unfortunately we cannot use SocketEvent::READ
		   here, so we just install a timer which retries after
		   100ms */

		retry_event.Schedule(file_retry_timeout);
	} else {
		/* XXX */
		Abort(std::make_exception_ptr(FormatErrno("Failed to read from '%s'",
							  path)));
	}
}

/*
 * istream implementation
 *
 */

off_t
FileIstream::_GetAvailable(bool partial) noexcept
{
	off_t available;
	if (rest != (off_t)-1)
		available = rest;
	else if (!partial)
		return (off_t)-1;
	else
		available = 0;

	available += buffer.GetAvailable();
	return available;
}

off_t
FileIstream::_Skip(off_t length) noexcept
{
	retry_event.Cancel();

	if (rest == (off_t)-1)
		return (off_t)-1;

	if (length == 0)
		return 0;

	const size_t buffer_available = buffer.GetAvailable();
	if (length < off_t(buffer_available)) {
		buffer.Consume(length);
		Consumed(length);
		return length;
	}

	length -= buffer_available;
	buffer.Clear();

	if (length >= rest) {
		/* skip beyond EOF */

		length = rest;
		rest = 0;
	} else {
		/* seek the file descriptor */

		if (fd.Skip(length) < 0)
			return -1;
		rest -= length;
	}

	off_t result = buffer_available + length;
	Consumed(result);
	return result;
}

int
FileIstream::_AsFd() noexcept
{
	int result_fd = fd.Steal();

	Destroy();

	return result_fd;
}

/*
 * constructor and public methods
 *
 */

UnusedIstreamPtr
istream_file_fd_new(EventLoop &event_loop, struct pool &pool,
		    const char *path,
		    UniqueFileDescriptor fd, FdType fd_type, off_t length) noexcept
{
	assert(fd.IsDefined());
	assert(length >= -1);

	return NewIstreamPtr<FileIstream>(pool, event_loop,
					  std::move(fd), fd_type,
					  length, path);
}

UnusedIstreamPtr
istream_file_new(EventLoop &event_loop, struct pool &pool,
		 const char *path)
{
	auto fd = OpenReadOnly(path);

	struct stat st;
	if (fstat(fd.Get(), &st) < 0)
		throw FormatErrno("Failed to stat %s", path);

	off_t size = S_ISREG(st.st_mode) ? st.st_size : -1;

	FdType fd_type = FdType::FD_FILE;
	if (S_ISCHR(st.st_mode))
		fd_type = FdType::FD_CHARDEV;
	else if (S_ISFIFO(st.st_mode))
		fd_type = FdType::FD_PIPE;
	else if (S_ISSOCK(st.st_mode))
		fd_type = FdType::FD_SOCKET;

	return istream_file_fd_new(event_loop, pool, path,
				   std::move(fd), fd_type, size);
}
<|endoftext|>


<commit_before>/*
 * This file is part of crash-reporter
 *
 * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
 *
 * Contact: Ville Ilvonen <ville.p.ilvonen@nokia.com>
 * Author: Riku Halonen <riku.halonen@nokia.com>
 *
 * Copyright (C) 2013 Jolla Ltd.
 * Contact: Jakub Adam <jakub.adam@jollamobile.com>
 *
 * This program 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 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA
 *
 */

// System includes.

#include <QAuthenticator>
#include <QJsonDocument>
#include <QJsonObject>
#include <QNetworkReply>
#include <QDebug>
#include <QFile>
#include <QFileInfo>
#include <QDir>
#include <QSslConfiguration>
#include <QNetworkProxy>
#include <QTime>

// User includes.

#include "creportercoreregistry.h"
#include "creporterhttpclient.h"
#include "creporterhttpclient_p.h"
#include "creporterapplicationsettings.h"
#include "creporterutils.h"

const char *clientstate_string[] = {"None", "Init", "Connecting", "Sending", "Aborting"};

// ******** Class CReporterHttpClientPrivate ********

// ======== MEMBER FUNCTIONS ========

// ----------------------------------------------------------------------------
// CReporterHttpClientPrivate::CReporterHttpClientPrivate
// ----------------------------------------------------------------------------
CReporterHttpClientPrivate::CReporterHttpClientPrivate() :
		m_manager( 0 ),
        m_reply( 0 )
{
		m_clientState = CReporterHttpClient::None;
}

// ----------------------------------------------------------------------------
// CReporterHttpClientPrivate::~CReporterHttpClientPrivate
// ----------------------------------------------------------------------------
CReporterHttpClientPrivate::~CReporterHttpClientPrivate()
{
    CReporterApplicationSettings::freeSingleton();

    if (m_reply != 0) {
        m_reply->abort();
        m_reply = 0;
    }

    if (m_manager != 0) {
		delete m_manager;
		m_manager = 0;
	}
}

// ----------------------------------------------------------------------------
// CReporterHttpClientPrivate::init
// ----------------------------------------------------------------------------
void CReporterHttpClientPrivate::init(bool deleteAfterSending)
{
	qDebug() << __PRETTY_FUNCTION__ << "Initiating HTTP session.";
	m_deleteFileFlag = deleteAfterSending;
	m_userAborted = false;
    m_httpError = false;

    if (CReporterApplicationSettings::instance()->useProxy()) {
        qDebug() << __PRETTY_FUNCTION__ << "Network proxy defined.";

        QNetworkProxy proxy;
        proxy.setType(QNetworkProxy::HttpProxy);
        proxy.setHostName(CReporterApplicationSettings::instance()->proxyUrl());
        proxy.setPort(CReporterApplicationSettings::instance()->proxyPort());
        QNetworkProxy::setApplicationProxy(proxy);
    }

    if (m_manager == 0) {
        m_manager = new QNetworkAccessManager(q_ptr);
        Q_CHECK_PTR(m_manager);

        connect(m_manager, SIGNAL(authenticationRequired(QNetworkReply*, QAuthenticator*)),
                this, SLOT(handleAuthenticationRequired(QNetworkReply*, QAuthenticator*)));
	}
   stateChange(CReporterHttpClient::Init);
}

// ----------------------------------------------------------------------------
// CReporterHttpClientPrivate::createRequest
// ----------------------------------------------------------------------------
bool CReporterHttpClientPrivate::createRequest(const QString &file)
{
    Q_ASSERT(m_manager != NULL);
    qDebug() << __PRETTY_FUNCTION__ << "Create new request.";

    if (m_clientState != CReporterHttpClient::Init) {
        return false;
    }

    QNetworkRequest request;
    QByteArray dataToSend;

    // Set server URL and port.
    QUrl url(CReporterApplicationSettings::instance()->serverUrl());

    url.setPort(CReporterApplicationSettings::instance()->serverPort());

    if (CReporterApplicationSettings::instance()->useSsl()) {
        qDebug() << __PRETTY_FUNCTION__ << "SSL is enabled.";
        QSslConfiguration ssl(QSslConfiguration::defaultConfiguration());
        ssl.setPeerVerifyMode(QSslSocket::VerifyNone);
        request.setSslConfiguration(ssl);
    }

    // Set file to be the current.
    m_currentFile.setFile(file);
    qDebug() << __PRETTY_FUNCTION__ << "File to upload:" << m_currentFile.absoluteFilePath();
    qDebug() << __PRETTY_FUNCTION__ << "File size:" << m_currentFile.size() / 1024 << "kB's";

    // For PUT, we need to append file name to the path.
    QString serverPath = CReporterApplicationSettings::instance()->serverPath() +
                         "/" + m_currentFile.fileName();

    url.setPath(serverPath);

    url.setQuery("uuid=" + CReporterUtils::deviceUid() +
            "&model=" + CReporterUtils::deviceModel());

    request.setUrl(url);
    qDebug() << __PRETTY_FUNCTION__ << "Upload URL:" << url.toString();

    if (!createPutRequest(request, dataToSend)) {
        qDebug() << __PRETTY_FUNCTION__ << "Failed to create network request.";
        return false;
    }

    // Send request and connect signal/ slots.
    m_reply = m_manager->put(request, dataToSend);

    if (m_reply == 0) {
        return false;
    }

    // Connect QNetworkReply signals.
    connect(m_reply, SIGNAL(sslErrors(QList<QSslError>)),
            this, SLOT(handleSslErrors(QList<QSslError>)));
    connect(m_reply, SIGNAL(error(QNetworkReply::NetworkError)),
            this, SLOT(handleError(QNetworkReply::NetworkError)));
    connect(m_reply, SIGNAL(finished()), this, SLOT(handleFinished()));

    stateChange(CReporterHttpClient::Connecting);
    return true;
}

// ----------------------------------------------------------------------------
// CReporterHttpClientPrivate::cancel
// ----------------------------------------------------------------------------
void CReporterHttpClientPrivate::cancel()
{
    qDebug() <<  __PRETTY_FUNCTION__ << "User aborted transaction.";
	m_userAborted = true;
	stateChange( CReporterHttpClient::Aborting );

    if (m_reply != 0) {
        qDebug() << __PRETTY_FUNCTION__ << "Canceling transaction.";
		// Abort ongoing transactions.
		m_reply->abort();
	}
	// Clean up.
	handleFinished();
}

// ======== LOCAL FUNCTIONS ========

// ----------------------------------------------------------------------------
// CReporterHttpClientPrivate::handleAuthenticationRequired
// ----------------------------------------------------------------------------
void CReporterHttpClientPrivate::handleAuthenticationRequired(QNetworkReply *reply,
                                                        QAuthenticator *authenticator)
{
	qDebug() << __PRETTY_FUNCTION__ << "Fill in the credentials.";

    connect(reply, SIGNAL(uploadProgress(qint64,qint64)),
            this, SLOT(handleUploadProgress(qint64,qint64)));

	// Fill in the credentials.
    authenticator->setUser(CReporterApplicationSettings::instance()->username());
    authenticator->setPassword(CReporterApplicationSettings::instance()->password());
}

// ----------------------------------------------------------------------------
// CReporterHttpClientPrivate::handleSslErrors
// ----------------------------------------------------------------------------
void CReporterHttpClientPrivate::handleSslErrors(const QList<QSslError> &errors )
{
	QString errorString;
	foreach (const QSslError &error, errors) {
		if ( !errorString.isEmpty() ) {
			errorString += ", ";
		 }
		 errorString += error.errorString();
	 }
	qDebug() << "One or more SSL errors occured:" << errorString;

	// Ignore and continue connection.
	m_reply->ignoreSslErrors();
}

// ----------------------------------------------------------------------------
// CReporterHttpClientPrivate::handleError
// ----------------------------------------------------------------------------
void CReporterHttpClientPrivate::handleError(QNetworkReply::NetworkError error)
{
    if (m_reply && m_reply->error() != QNetworkReply::NoError) {
		// Finished is emitted by QNetworkReply after this, inidicating that
		// the connection is over.
		qCritical() << __PRETTY_FUNCTION__ << "Upload failed.";
        m_httpError = true;
		QString errorString =m_reply->errorString();
        qDebug() << __PRETTY_FUNCTION__ << "Error code:" << error << "," << errorString;
        emit uploadError(m_currentFile.fileName(), errorString);
	}
}

void CReporterHttpClientPrivate::parseReply()
{
    if (!m_reply) {
        qDebug() << "Server reply is NULL";
        return;
    }

    if (!m_reply->open(QIODevice::ReadOnly)) {
        qDebug() << "Couldn't open server reply for reading.";
        return;
    }

    QJsonDocument reply = QJsonDocument::fromJson(m_reply->readAll());
    if (reply.isNull() || !reply.isObject()) {
        qDebug() << "Error parsing JSON server reply.";
        return;
    }

    QJsonObject json = reply.object();
    int submissionId = static_cast<int>(json.value("submission_id").toDouble(0));
    if (submissionId == 0) {
        qDebug() << "Failed to parse submission id from JSON.";
        return;
    }

    QUrl submissionUrl(CReporterApplicationSettings::instance()->serverUrl());
    submissionUrl.setPort(CReporterApplicationSettings::instance()->serverPort());
    submissionUrl.setPath("/");
    submissionUrl.setFragment(QString("submissions/%1").arg(submissionId));

    QString corePath(CReporterCoreRegistry::instance()->getCoreLocationPaths().first());
    QFile uploadlog(corePath + "/uploadlog");
    if (!uploadlog.open(QIODevice::WriteOnly | QIODevice::Append)) {
        qDebug() << "Couldn't open uploadlog for writing.";
        return;
    }

    QTextStream stream(&uploadlog);
    stream << m_currentFile.fileName() << ' ' << submissionUrl.toString() << '\n';

    uploadlog.close();
}

// ----------------------------------------------------------------------------
// CReporterHttpClientPrivate::handleFinished
// ----------------------------------------------------------------------------
void CReporterHttpClientPrivate::handleFinished()
{
    qDebug() << __PRETTY_FUNCTION__ << "Uploading file:" << m_currentFile.fileName() << "finished.";

    if (!m_httpError && !m_userAborted) {
        // Upload was successful.
        parseReply();

        if (m_deleteFileFlag) {
            // Remove file if delete was requested.
            CReporterUtils::removeFile(m_currentFile.absoluteFilePath());
        }
	}

    // QNetworkreply objects are owned by QNetworkAccessManager and deleted along with it.
	m_reply = 0;

    stateChange(CReporterHttpClient::Init);
    emit finished();
}

// ----------------------------------------------------------------------------
// CReporterHttpClientPrivate::handleUploadProgress
// ----------------------------------------------------------------------------
void CReporterHttpClientPrivate::handleUploadProgress(qint64 bytesSent, qint64 bytesTotal)
{
    qDebug() << __PRETTY_FUNCTION__ << "Sent:" << bytesSent << "Total:" << bytesTotal;

    if (m_userAborted || m_httpError) {
        // Do not update, if aborted.
        return;
    }

    if (m_clientState != CReporterHttpClient::Sending) {
        stateChange(CReporterHttpClient::Sending);
    }

    int done = (int)((bytesSent * 100) / bytesTotal);
    qDebug() << __PRETTY_FUNCTION__ << "Done:" << done << "%";
    emit q_ptr->updateProgress(done);
}

// ----------------------------------------------------------------------------
// CReporterHttpClientPrivate::stateChange
// ----------------------------------------------------------------------------
void CReporterHttpClientPrivate::stateChange(CReporterHttpClient::State nextState)
{
    qDebug() << __PRETTY_FUNCTION__ << "Current state:" << q_ptr->stateToString( m_clientState );
    qDebug() << __PRETTY_FUNCTION__ << "New state:" << q_ptr->stateToString( nextState );
    m_clientState = nextState;
    emit stateChanged( m_clientState );
}

// ----------------------------------------------------------------------------
// CReporterHttpClientPrivate::createPutRequest
// ----------------------------------------------------------------------------
bool CReporterHttpClientPrivate::createPutRequest(QNetworkRequest &request,
                                                   QByteArray &dataToSend)
{
    QFile file(m_currentFile.absoluteFilePath());
    // Abort, if file doesn't exist or IO error.
    if (!file.exists() || !file.open(QIODevice::ReadOnly)) {
        return false;
    }

    // Append file.
    dataToSend += file.readAll();
    file.close();

    // Construct HTTP Headers.
    request.setRawHeader("User-Agent", "crash-reporter");
    request.setRawHeader("Accept", "*/*");
    request.setHeader(QNetworkRequest::ContentLengthHeader, dataToSend.size());

    return true;
}

// ******** Class CReporterHttpClient ********

// ======== MEMBER FUNCTIONS ========

// ----------------------------------------------------------------------------
// CReporterHttpClient::CReporterHttpClient
// ----------------------------------------------------------------------------
CReporterHttpClient::CReporterHttpClient(QObject *parent) :
        QObject(parent) ,
        d_ptr(new CReporterHttpClientPrivate())
{
    Q_D(CReporterHttpClient);
	d->q_ptr = this;

	connect( d, SIGNAL(stateChanged(CReporterHttpClient::State)),
			 this, SIGNAL(clientStateChanged(CReporterHttpClient::State)) );
	connect( d, SIGNAL(uploadError(QString,QString)), this, SIGNAL(uploadError(QString,QString)) );
    connect( d, SIGNAL(finished()), this, SIGNAL(finished()) );
}

// ----------------------------------------------------------------------------
// CReporterHttpClient::~CReporterHttpClient
// ----------------------------------------------------------------------------
CReporterHttpClient::~CReporterHttpClient()
{
    qDebug() << __PRETTY_FUNCTION__ << "Client destroyed.";

	delete d_ptr;
	d_ptr = NULL;
}

// ----------------------------------------------------------------------------
// CReporterHttpClient::initSession
// ----------------------------------------------------------------------------
void CReporterHttpClient::initSession(bool deleteAfterSending)
{
    Q_D(CReporterHttpClient);
    d->init(deleteAfterSending);
}

// ----------------------------------------------------------------------------
// CReporterHttpClient::state
// ----------------------------------------------------------------------------
CReporterHttpClient::State CReporterHttpClient::state() const
{
	return d_ptr->m_clientState;
}

// ----------------------------------------------------------------------------
// CReporterHttpClient::stateToString
// ----------------------------------------------------------------------------
QString CReporterHttpClient::stateToString(CReporterHttpClient::State state) const
{
    return  QString(clientstate_string[state]);
}

// ----------------------------------------------------------------------------
// CReporterHttpClient::upload
// ----------------------------------------------------------------------------
bool CReporterHttpClient::upload(const QString &file)
{
	Q_D( CReporterHttpClient );
	qDebug() << __PRETTY_FUNCTION__ << "Upload requested.";

    return d->createRequest(file);
}

// ----------------------------------------------------------------------------
// CReporterHttpClient::cancel
// ----------------------------------------------------------------------------
void CReporterHttpClient::cancel()
{
    Q_D(CReporterHttpClient);
	qDebug() << __PRETTY_FUNCTION__ << "Cancel requested.";

    d->cancel();
}

// End of file
<commit_msg>[httpclient] connect to QNetworkReply::uploadProgress() immediately<commit_after>/*
 * This file is part of crash-reporter
 *
 * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
 *
 * Contact: Ville Ilvonen <ville.p.ilvonen@nokia.com>
 * Author: Riku Halonen <riku.halonen@nokia.com>
 *
 * Copyright (C) 2013 Jolla Ltd.
 * Contact: Jakub Adam <jakub.adam@jollamobile.com>
 *
 * This program 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 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA
 *
 */

// System includes.

#include <QAuthenticator>
#include <QJsonDocument>
#include <QJsonObject>
#include <QNetworkReply>
#include <QDebug>
#include <QFile>
#include <QFileInfo>
#include <QDir>
#include <QSslConfiguration>
#include <QNetworkProxy>
#include <QTime>

// User includes.

#include "creportercoreregistry.h"
#include "creporterhttpclient.h"
#include "creporterhttpclient_p.h"
#include "creporterapplicationsettings.h"
#include "creporterutils.h"

const char *clientstate_string[] = {"None", "Init", "Connecting", "Sending", "Aborting"};

// ******** Class CReporterHttpClientPrivate ********

// ======== MEMBER FUNCTIONS ========

// ----------------------------------------------------------------------------
// CReporterHttpClientPrivate::CReporterHttpClientPrivate
// ----------------------------------------------------------------------------
CReporterHttpClientPrivate::CReporterHttpClientPrivate() :
		m_manager( 0 ),
        m_reply( 0 )
{
		m_clientState = CReporterHttpClient::None;
}

// ----------------------------------------------------------------------------
// CReporterHttpClientPrivate::~CReporterHttpClientPrivate
// ----------------------------------------------------------------------------
CReporterHttpClientPrivate::~CReporterHttpClientPrivate()
{
    CReporterApplicationSettings::freeSingleton();

    if (m_reply != 0) {
        m_reply->abort();
        m_reply = 0;
    }

    if (m_manager != 0) {
		delete m_manager;
		m_manager = 0;
	}
}

// ----------------------------------------------------------------------------
// CReporterHttpClientPrivate::init
// ----------------------------------------------------------------------------
void CReporterHttpClientPrivate::init(bool deleteAfterSending)
{
	qDebug() << __PRETTY_FUNCTION__ << "Initiating HTTP session.";
	m_deleteFileFlag = deleteAfterSending;
	m_userAborted = false;
    m_httpError = false;

    if (CReporterApplicationSettings::instance()->useProxy()) {
        qDebug() << __PRETTY_FUNCTION__ << "Network proxy defined.";

        QNetworkProxy proxy;
        proxy.setType(QNetworkProxy::HttpProxy);
        proxy.setHostName(CReporterApplicationSettings::instance()->proxyUrl());
        proxy.setPort(CReporterApplicationSettings::instance()->proxyPort());
        QNetworkProxy::setApplicationProxy(proxy);
    }

    if (m_manager == 0) {
        m_manager = new QNetworkAccessManager(q_ptr);
        Q_CHECK_PTR(m_manager);

        connect(m_manager, SIGNAL(authenticationRequired(QNetworkReply*, QAuthenticator*)),
                this, SLOT(handleAuthenticationRequired(QNetworkReply*, QAuthenticator*)));
	}
   stateChange(CReporterHttpClient::Init);
}

// ----------------------------------------------------------------------------
// CReporterHttpClientPrivate::createRequest
// ----------------------------------------------------------------------------
bool CReporterHttpClientPrivate::createRequest(const QString &file)
{
    Q_ASSERT(m_manager != NULL);
    qDebug() << __PRETTY_FUNCTION__ << "Create new request.";

    if (m_clientState != CReporterHttpClient::Init) {
        return false;
    }

    QNetworkRequest request;
    QByteArray dataToSend;

    // Set server URL and port.
    QUrl url(CReporterApplicationSettings::instance()->serverUrl());

    url.setPort(CReporterApplicationSettings::instance()->serverPort());

    if (CReporterApplicationSettings::instance()->useSsl()) {
        qDebug() << __PRETTY_FUNCTION__ << "SSL is enabled.";
        QSslConfiguration ssl(QSslConfiguration::defaultConfiguration());
        ssl.setPeerVerifyMode(QSslSocket::VerifyNone);
        request.setSslConfiguration(ssl);
    }

    // Set file to be the current.
    m_currentFile.setFile(file);
    qDebug() << __PRETTY_FUNCTION__ << "File to upload:" << m_currentFile.absoluteFilePath();
    qDebug() << __PRETTY_FUNCTION__ << "File size:" << m_currentFile.size() / 1024 << "kB's";

    // For PUT, we need to append file name to the path.
    QString serverPath = CReporterApplicationSettings::instance()->serverPath() +
                         "/" + m_currentFile.fileName();

    url.setPath(serverPath);

    url.setQuery("uuid=" + CReporterUtils::deviceUid() +
            "&model=" + CReporterUtils::deviceModel());

    request.setUrl(url);
    qDebug() << __PRETTY_FUNCTION__ << "Upload URL:" << url.toString();

    if (!createPutRequest(request, dataToSend)) {
        qDebug() << __PRETTY_FUNCTION__ << "Failed to create network request.";
        return false;
    }

    // Send request and connect signal/ slots.
    m_reply = m_manager->put(request, dataToSend);

    if (m_reply == 0) {
        return false;
    }

    // Connect QNetworkReply signals.
    connect(m_reply, SIGNAL(sslErrors(QList<QSslError>)),
            this, SLOT(handleSslErrors(QList<QSslError>)));
    connect(m_reply, SIGNAL(error(QNetworkReply::NetworkError)),
            this, SLOT(handleError(QNetworkReply::NetworkError)));
    connect(m_reply, SIGNAL(finished()), this, SLOT(handleFinished()));
    connect(m_reply, &QNetworkReply::uploadProgress,
            this, &CReporterHttpClientPrivate::handleUploadProgress);

    stateChange(CReporterHttpClient::Connecting);
    return true;
}

// ----------------------------------------------------------------------------
// CReporterHttpClientPrivate::cancel
// ----------------------------------------------------------------------------
void CReporterHttpClientPrivate::cancel()
{
    qDebug() <<  __PRETTY_FUNCTION__ << "User aborted transaction.";
	m_userAborted = true;
	stateChange( CReporterHttpClient::Aborting );

    if (m_reply != 0) {
        qDebug() << __PRETTY_FUNCTION__ << "Canceling transaction.";
		// Abort ongoing transactions.
		m_reply->abort();
	}
	// Clean up.
	handleFinished();
}

// ======== LOCAL FUNCTIONS ========

// ----------------------------------------------------------------------------
// CReporterHttpClientPrivate::handleAuthenticationRequired
// ----------------------------------------------------------------------------
void CReporterHttpClientPrivate::handleAuthenticationRequired(QNetworkReply *reply,
                                                        QAuthenticator *authenticator)
{
    Q_UNUSED(reply)

	qDebug() << __PRETTY_FUNCTION__ << "Fill in the credentials.";

	// Fill in the credentials.
    authenticator->setUser(CReporterApplicationSettings::instance()->username());
    authenticator->setPassword(CReporterApplicationSettings::instance()->password());
}

// ----------------------------------------------------------------------------
// CReporterHttpClientPrivate::handleSslErrors
// ----------------------------------------------------------------------------
void CReporterHttpClientPrivate::handleSslErrors(const QList<QSslError> &errors )
{
	QString errorString;
	foreach (const QSslError &error, errors) {
		if ( !errorString.isEmpty() ) {
			errorString += ", ";
		 }
		 errorString += error.errorString();
	 }
	qDebug() << "One or more SSL errors occured:" << errorString;

	// Ignore and continue connection.
	m_reply->ignoreSslErrors();
}

// ----------------------------------------------------------------------------
// CReporterHttpClientPrivate::handleError
// ----------------------------------------------------------------------------
void CReporterHttpClientPrivate::handleError(QNetworkReply::NetworkError error)
{
    if (m_reply && m_reply->error() != QNetworkReply::NoError) {
		// Finished is emitted by QNetworkReply after this, inidicating that
		// the connection is over.
		qCritical() << __PRETTY_FUNCTION__ << "Upload failed.";
        m_httpError = true;
		QString errorString =m_reply->errorString();
        qDebug() << __PRETTY_FUNCTION__ << "Error code:" << error << "," << errorString;
        emit uploadError(m_currentFile.fileName(), errorString);
	}
}

void CReporterHttpClientPrivate::parseReply()
{
    if (!m_reply) {
        qDebug() << "Server reply is NULL";
        return;
    }

    if (!m_reply->open(QIODevice::ReadOnly)) {
        qDebug() << "Couldn't open server reply for reading.";
        return;
    }

    QJsonDocument reply = QJsonDocument::fromJson(m_reply->readAll());
    if (reply.isNull() || !reply.isObject()) {
        qDebug() << "Error parsing JSON server reply.";
        return;
    }

    QJsonObject json = reply.object();
    int submissionId = static_cast<int>(json.value("submission_id").toDouble(0));
    if (submissionId == 0) {
        qDebug() << "Failed to parse submission id from JSON.";
        return;
    }

    QUrl submissionUrl(CReporterApplicationSettings::instance()->serverUrl());
    submissionUrl.setPort(CReporterApplicationSettings::instance()->serverPort());
    submissionUrl.setPath("/");
    submissionUrl.setFragment(QString("submissions/%1").arg(submissionId));

    QString corePath(CReporterCoreRegistry::instance()->getCoreLocationPaths().first());
    QFile uploadlog(corePath + "/uploadlog");
    if (!uploadlog.open(QIODevice::WriteOnly | QIODevice::Append)) {
        qDebug() << "Couldn't open uploadlog for writing.";
        return;
    }

    QTextStream stream(&uploadlog);
    stream << m_currentFile.fileName() << ' ' << submissionUrl.toString() << '\n';

    uploadlog.close();
}

// ----------------------------------------------------------------------------
// CReporterHttpClientPrivate::handleFinished
// ----------------------------------------------------------------------------
void CReporterHttpClientPrivate::handleFinished()
{
    qDebug() << __PRETTY_FUNCTION__ << "Uploading file:" << m_currentFile.fileName() << "finished.";

    if (!m_httpError && !m_userAborted) {
        // Upload was successful.
        parseReply();

        if (m_deleteFileFlag) {
            // Remove file if delete was requested.
            CReporterUtils::removeFile(m_currentFile.absoluteFilePath());
        }
	}

    // QNetworkreply objects are owned by QNetworkAccessManager and deleted along with it.
	m_reply = 0;

    stateChange(CReporterHttpClient::Init);
    emit finished();
}

// ----------------------------------------------------------------------------
// CReporterHttpClientPrivate::handleUploadProgress
// ----------------------------------------------------------------------------
void CReporterHttpClientPrivate::handleUploadProgress(qint64 bytesSent, qint64 bytesTotal)
{
    qDebug() << __PRETTY_FUNCTION__ << "Sent:" << bytesSent << "Total:" << bytesTotal;

    if (m_userAborted || m_httpError) {
        // Do not update, if aborted.
        return;
    }

    if (m_clientState != CReporterHttpClient::Sending) {
        stateChange(CReporterHttpClient::Sending);
    }

    int done = (int)((bytesSent * 100) / bytesTotal);
    qDebug() << __PRETTY_FUNCTION__ << "Done:" << done << "%";
    emit q_ptr->updateProgress(done);
}

// ----------------------------------------------------------------------------
// CReporterHttpClientPrivate::stateChange
// ----------------------------------------------------------------------------
void CReporterHttpClientPrivate::stateChange(CReporterHttpClient::State nextState)
{
    qDebug() << __PRETTY_FUNCTION__ << "Current state:" << q_ptr->stateToString( m_clientState );
    qDebug() << __PRETTY_FUNCTION__ << "New state:" << q_ptr->stateToString( nextState );
    m_clientState = nextState;
    emit stateChanged( m_clientState );
}

// ----------------------------------------------------------------------------
// CReporterHttpClientPrivate::createPutRequest
// ----------------------------------------------------------------------------
bool CReporterHttpClientPrivate::createPutRequest(QNetworkRequest &request,
                                                   QByteArray &dataToSend)
{
    QFile file(m_currentFile.absoluteFilePath());
    // Abort, if file doesn't exist or IO error.
    if (!file.exists() || !file.open(QIODevice::ReadOnly)) {
        return false;
    }

    // Append file.
    dataToSend += file.readAll();
    file.close();

    // Construct HTTP Headers.
    request.setRawHeader("User-Agent", "crash-reporter");
    request.setRawHeader("Accept", "*/*");
    request.setHeader(QNetworkRequest::ContentLengthHeader, dataToSend.size());

    return true;
}

// ******** Class CReporterHttpClient ********

// ======== MEMBER FUNCTIONS ========

// ----------------------------------------------------------------------------
// CReporterHttpClient::CReporterHttpClient
// ----------------------------------------------------------------------------
CReporterHttpClient::CReporterHttpClient(QObject *parent) :
        QObject(parent) ,
        d_ptr(new CReporterHttpClientPrivate())
{
    Q_D(CReporterHttpClient);
	d->q_ptr = this;

	connect( d, SIGNAL(stateChanged(CReporterHttpClient::State)),
			 this, SIGNAL(clientStateChanged(CReporterHttpClient::State)) );
	connect( d, SIGNAL(uploadError(QString,QString)), this, SIGNAL(uploadError(QString,QString)) );
    connect( d, SIGNAL(finished()), this, SIGNAL(finished()) );
}

// ----------------------------------------------------------------------------
// CReporterHttpClient::~CReporterHttpClient
// ----------------------------------------------------------------------------
CReporterHttpClient::~CReporterHttpClient()
{
    qDebug() << __PRETTY_FUNCTION__ << "Client destroyed.";

	delete d_ptr;
	d_ptr = NULL;
}

// ----------------------------------------------------------------------------
// CReporterHttpClient::initSession
// ----------------------------------------------------------------------------
void CReporterHttpClient::initSession(bool deleteAfterSending)
{
    Q_D(CReporterHttpClient);
    d->init(deleteAfterSending);
}

// ----------------------------------------------------------------------------
// CReporterHttpClient::state
// ----------------------------------------------------------------------------
CReporterHttpClient::State CReporterHttpClient::state() const
{
	return d_ptr->m_clientState;
}

// ----------------------------------------------------------------------------
// CReporterHttpClient::stateToString
// ----------------------------------------------------------------------------
QString CReporterHttpClient::stateToString(CReporterHttpClient::State state) const
{
    return  QString(clientstate_string[state]);
}

// ----------------------------------------------------------------------------
// CReporterHttpClient::upload
// ----------------------------------------------------------------------------
bool CReporterHttpClient::upload(const QString &file)
{
	Q_D( CReporterHttpClient );
	qDebug() << __PRETTY_FUNCTION__ << "Upload requested.";

    return d->createRequest(file);
}

// ----------------------------------------------------------------------------
// CReporterHttpClient::cancel
// ----------------------------------------------------------------------------
void CReporterHttpClient::cancel()
{
    Q_D(CReporterHttpClient);
	qDebug() << __PRETTY_FUNCTION__ << "Cancel requested.";

    d->cancel();
}

// End of file
<|endoftext|>


<commit_before>#include "Capture.h"
#include "Timer.h"
#include <iostream>
#include <time.h>

static const int cameraWidth = 848, cameraHeight = 480;

//static const int videoFileFPS = 60; // Set frame rate to 60 fps for ffmpeg.
static const int videoFileFPS = -1; // Let GStreamer control its own frame rate.

Capture::Capture(int device)
    : cap(device)
    , fps(-1)
    , canDropFrame(true)
{
    imageWidth  = cameraWidth;
    imageHeight = cameraHeight;

    cap.set(cv::CAP_PROP_FRAME_WIDTH,  cameraWidth);
    cap.set(cv::CAP_PROP_FRAME_HEIGHT, cameraHeight);
}

Capture::Capture(const char* filename)
    : cap(filename)
    , fps(videoFileFPS)
    , canDropFrame(false)
{
    imageWidth  = cap.get(cv::CAP_PROP_FRAME_WIDTH);
    imageHeight = cap.get(cv::CAP_PROP_FRAME_HEIGHT);
}

void Capture::BackgroundLoop() {
    cv::Mat image;
    int frameCount = 0;
    Timer timer;

    timer.Start();
    while (! imagesCaptured.quitting) {

        if (! cap.read(image)) {break;}

        if (canDropFrame && imagesCaptured.size >= imagesCaptured.Capacity()) {
            std::cerr << "Capture dropped frame." << std::endl;
        } else {
            imagesCaptured.Enqueue(image);

            ++frameCount;
            struct timespec sleep = {0, fps > 0 ? 1000 * timer.NextSleep(fps, frameCount) : 0};
            if (sleep.tv_nsec > 0) {
                nanosleep(&sleep, NULL);
            }
        }
    }
    timer.PrintTimeStats(frameCount);
}

static void* CaptureThread(void* cap) {
    ((Capture*)cap)->BackgroundLoop();
    return 0;
}

void Capture::Start() {
    pthread_create(&thread, NULL, CaptureThread, this);
}

void Capture::Stop() {
    imagesCaptured.Quit();
    pthread_join(thread, NULL);
}
<commit_msg>frame rate and image size<commit_after>#include "Capture.h"
#include "Timer.h"
#include <iostream>
#include <time.h>

static const int cameraWidth = 160, cameraHeight = 120;
static const float cameraFPS  = -1.;
static const int videoFileFPS = -1;

Capture::Capture(int device)
    : cap(device)
    , fps(-1)
    , canDropFrame(true)
{
#if 0
    imageWidth  = cap.get(cv::CAP_PROP_FRAME_WIDTH);
    imageHeight = cap.get(cv::CAP_PROP_FRAME_HEIGHT);
    std::cerr << "camera: " << imageWidth << " x " << imageHeight << std::endl;
#else
    imageWidth  = cameraWidth;
    imageHeight = cameraHeight;

    cap.set(cv::CAP_PROP_FRAME_WIDTH,  cameraWidth);
    cap.set(cv::CAP_PROP_FRAME_HEIGHT, cameraHeight);
#endif

    if (cameraFPS > 0) {
        cap.set(cv::CAP_PROP_FPS, cameraFPS);
    }
}

Capture::Capture(const char* filename)
    : cap(filename)
    , fps(videoFileFPS)
    , canDropFrame(false)
{
    imageWidth  = cap.get(cv::CAP_PROP_FRAME_WIDTH);
    imageHeight = cap.get(cv::CAP_PROP_FRAME_HEIGHT);
}

void Capture::BackgroundLoop() {
    cv::Mat image;
    int frameCount = 0;
    Timer timer;

    timer.Start();
    while (! imagesCaptured.quitting) {

        if (! cap.read(image)) {break;}

        if (canDropFrame && imagesCaptured.size >= imagesCaptured.Capacity()) {
            std::cerr << "Capture dropped frame." << std::endl;
        } else {
            imagesCaptured.Enqueue(image);

            ++frameCount;
            struct timespec sleep = {0, fps > 0 ? 1000 * timer.NextSleep(fps, frameCount) : 0};
            if (sleep.tv_nsec > 0) {
                nanosleep(&sleep, NULL);
            }
        }
    }
    timer.PrintTimeStats(frameCount);
}

static void* CaptureThread(void* cap) {
    ((Capture*)cap)->BackgroundLoop();
    return 0;
}

void Capture::Start() {
    pthread_create(&thread, NULL, CaptureThread, this);
}

void Capture::Stop() {
    imagesCaptured.Quit();
    pthread_join(thread, NULL);
}
<|endoftext|>


<commit_before>// Copyright 2015 Open Source Robotics Foundation, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef RCLCPP__INTRA_PROCESS_MANAGER_IMPL_HPP_
#define RCLCPP__INTRA_PROCESS_MANAGER_IMPL_HPP_

#include <algorithm>
#include <array>
#include <atomic>
#include <cstring>
#include <functional>
#include <limits>
#include <map>
#include <memory>
#include <mutex>
#include <set>
#include <string>
#include <unordered_map>
#include <utility>

#include "rmw/validate_full_topic_name.h"

#include "rclcpp/macros.hpp"
#include "rclcpp/mapped_ring_buffer.hpp"
#include "rclcpp/publisher.hpp"
#include "rclcpp/subscription.hpp"
#include "rclcpp/visibility_control.hpp"

namespace rclcpp
{
namespace intra_process_manager
{

class IntraProcessManagerImplBase
{
public:
  RCLCPP_SMART_PTR_DEFINITIONS_NOT_COPYABLE(IntraProcessManagerImplBase)

  IntraProcessManagerImplBase() = default;
  virtual ~IntraProcessManagerImplBase() = default;

  virtual void
  add_subscription(uint64_t id, SubscriptionBase::SharedPtr subscription) = 0;

  virtual void
  remove_subscription(uint64_t intra_process_subscription_id) = 0;

  virtual void add_publisher(
    uint64_t id,
    PublisherBase::WeakPtr publisher,
    mapped_ring_buffer::MappedRingBufferBase::SharedPtr mrb,
    size_t size) = 0;

  virtual void
  remove_publisher(uint64_t intra_process_publisher_id) = 0;

  virtual mapped_ring_buffer::MappedRingBufferBase::SharedPtr
  get_publisher_info_for_id(
    uint64_t intra_process_publisher_id,
    uint64_t & message_seq) = 0;

  virtual void
  store_intra_process_message(uint64_t intra_process_publisher_id, uint64_t message_seq) = 0;

  virtual mapped_ring_buffer::MappedRingBufferBase::SharedPtr
  take_intra_process_message(
    uint64_t intra_process_publisher_id,
    uint64_t message_sequence_number,
    uint64_t requesting_subscriptions_intra_process_id,
    size_t & size) = 0;

  virtual bool
  matches_any_publishers(const rmw_gid_t * id) const = 0;

  virtual size_t
  get_subscription_count(uint64_t intra_process_publisher_id) const = 0;

private:
  RCLCPP_DISABLE_COPY(IntraProcessManagerImplBase)
};

template<typename Allocator = std::allocator<void>>
class IntraProcessManagerImpl : public IntraProcessManagerImplBase
{
public:
  IntraProcessManagerImpl() = default;
  ~IntraProcessManagerImpl() = default;

  void
  add_subscription(uint64_t id, SubscriptionBase::SharedPtr subscription)
  {
    subscriptions_[id] = subscription;
    subscription_ids_by_topic_[fixed_size_string(subscription->get_topic_name())].insert(id);
  }

  void
  remove_subscription(uint64_t intra_process_subscription_id)
  {
    subscriptions_.erase(intra_process_subscription_id);
    for (auto & pair : subscription_ids_by_topic_) {
      pair.second.erase(intra_process_subscription_id);
    }
    // Iterate over all publisher infos and all stored subscription id's and
    // remove references to this subscription's id.
    for (auto & publisher_pair : publishers_) {
      for (auto & sub_pair : publisher_pair.second.target_subscriptions_by_message_sequence) {
        sub_pair.second.erase(intra_process_subscription_id);
      }
    }
  }

  void add_publisher(
    uint64_t id,
    PublisherBase::WeakPtr publisher,
    mapped_ring_buffer::MappedRingBufferBase::SharedPtr mrb,
    size_t size)
  {
    publishers_[id].publisher = publisher;
    // As long as the size of the ring buffer is less than the max sequence number, we're safe.
    if (size > std::numeric_limits<uint64_t>::max()) {
      throw std::invalid_argument("the calculated buffer size is too large");
    }
    publishers_[id].sequence_number.store(0);

    publishers_[id].buffer = mrb;
    publishers_[id].target_subscriptions_by_message_sequence.reserve(size);
  }

  void
  remove_publisher(uint64_t intra_process_publisher_id)
  {
    publishers_.erase(intra_process_publisher_id);
  }

  // return message_seq and mrb
  mapped_ring_buffer::MappedRingBufferBase::SharedPtr
  get_publisher_info_for_id(
    uint64_t intra_process_publisher_id,
    uint64_t & message_seq)
  {
    std::lock_guard<std::mutex> lock(runtime_mutex_);
    auto it = publishers_.find(intra_process_publisher_id);
    if (it == publishers_.end()) {
      throw std::runtime_error("get_publisher_info_for_id called with invalid publisher id");
    }
    PublisherInfo & info = it->second;
    // Calculate the next message sequence number.
    message_seq = info.sequence_number.fetch_add(1);

    return info.buffer;
  }

  void
  store_intra_process_message(uint64_t intra_process_publisher_id, uint64_t message_seq)
  {
    std::lock_guard<std::mutex> lock(runtime_mutex_);
    auto it = publishers_.find(intra_process_publisher_id);
    if (it == publishers_.end()) {
      throw std::runtime_error("store_intra_process_message called with invalid publisher id");
    }
    PublisherInfo & info = it->second;
    auto publisher = info.publisher.lock();
    if (!publisher) {
      throw std::runtime_error("publisher has unexpectedly gone out of scope");
    }

    // Figure out what subscriptions should receive the message.
    auto & destined_subscriptions =
      subscription_ids_by_topic_[fixed_size_string(publisher->get_topic_name())];
    // Store the list for later comparison.
    if (info.target_subscriptions_by_message_sequence.count(message_seq) == 0) {
      info.target_subscriptions_by_message_sequence.emplace(
        message_seq, AllocSet(std::less<uint64_t>(), uint64_allocator));
    } else {
      info.target_subscriptions_by_message_sequence[message_seq].clear();
    }
    std::copy(
      destined_subscriptions.begin(), destined_subscriptions.end(),
      // Memory allocation occurs in info.target_subscriptions_by_message_sequence[message_seq]
      std::inserter(
        info.target_subscriptions_by_message_sequence[message_seq],
        // This ends up only being a hint to std::set, could also be .begin().
        info.target_subscriptions_by_message_sequence[message_seq].end()
      )
    );
  }

  mapped_ring_buffer::MappedRingBufferBase::SharedPtr
  take_intra_process_message(
    uint64_t intra_process_publisher_id,
    uint64_t message_sequence_number,
    uint64_t requesting_subscriptions_intra_process_id,
    size_t & size
  )
  {
    std::lock_guard<std::mutex> lock(runtime_mutex_);
    PublisherInfo * info;
    {
      auto it = publishers_.find(intra_process_publisher_id);
      if (it == publishers_.end()) {
        // Publisher is either invalid or no longer exists.
        return 0;
      }
      info = &it->second;
    }
    // Figure out how many subscriptions are left.
    AllocSet * target_subs;
    {
      auto it = info->target_subscriptions_by_message_sequence.find(message_sequence_number);
      if (it == info->target_subscriptions_by_message_sequence.end()) {
        // Message is no longer being stored by this publisher.
        return 0;
      }
      target_subs = &it->second;
    }
    {
      auto it = std::find(
        target_subs->begin(), target_subs->end(),
        requesting_subscriptions_intra_process_id);
      if (it == target_subs->end()) {
        // This publisher id/message seq pair was not intended for this subscription.
        return 0;
      }
      target_subs->erase(it);
    }
    size = target_subs->size();
    return info->buffer;
  }

  bool
  matches_any_publishers(const rmw_gid_t * id) const
  {
    for (auto & publisher_pair : publishers_) {
      auto publisher = publisher_pair.second.publisher.lock();
      if (!publisher) {
        continue;
      }
      if (*publisher.get() == id) {
        return true;
      }
    }
    return false;
  }

  size_t
  get_subscription_count(uint64_t intra_process_publisher_id) const
  {
    auto publisher_it = publishers_.find(intra_process_publisher_id);
    if (publisher_it == publishers_.end()) {
      // Publisher is either invalid or no longer exists.
      return 0;
    }
    auto publisher = publisher_it->second.publisher.lock();
    if (!publisher) {
      throw std::runtime_error("publisher has unexpectedly gone out of scope");
    }
    auto sub_map_it =
      subscription_ids_by_topic_.find(fixed_size_string(publisher->get_topic_name()));
    if (sub_map_it == subscription_ids_by_topic_.end()) {
      // No intraprocess subscribers
      return 0;
    }
    return sub_map_it->second.size();
  }

private:
  RCLCPP_DISABLE_COPY(IntraProcessManagerImpl)

  using FixedSizeString = std::array<char, RMW_TOPIC_MAX_NAME_LENGTH + 1>;

  FixedSizeString
  fixed_size_string(const char * str) const
  {
    FixedSizeString ret;
    std::strncpy(ret.data(), str, ret.size());
    return ret;
  }

  template<typename T>
  using RebindAlloc = typename std::allocator_traits<Allocator>::template rebind_alloc<T>;

  RebindAlloc<uint64_t> uint64_allocator;

  using AllocSet = std::set<uint64_t, std::less<uint64_t>, RebindAlloc<uint64_t>>;
  using SubscriptionMap = std::unordered_map<
    uint64_t, SubscriptionBase::WeakPtr,
    std::hash<uint64_t>, std::equal_to<uint64_t>,
    RebindAlloc<std::pair<const uint64_t, SubscriptionBase::WeakPtr>>>;

  using IDTopicMap = std::map<
    FixedSizeString,
    AllocSet,
    std::less<FixedSizeString>,
    RebindAlloc<std::pair<const FixedSizeString, AllocSet>>>;

  SubscriptionMap subscriptions_;

  IDTopicMap subscription_ids_by_topic_;

  struct PublisherInfo
  {
    RCLCPP_DISABLE_COPY(PublisherInfo)

    PublisherInfo() = default;

    PublisherBase::WeakPtr publisher;
    std::atomic<uint64_t> sequence_number;
    mapped_ring_buffer::MappedRingBufferBase::SharedPtr buffer;

    using TargetSubscriptionsMap = std::unordered_map<
      uint64_t, AllocSet,
      std::hash<uint64_t>, std::equal_to<uint64_t>,
      RebindAlloc<std::pair<const uint64_t, AllocSet>>>;
    TargetSubscriptionsMap target_subscriptions_by_message_sequence;
  };

  using PublisherMap = std::unordered_map<
    uint64_t, PublisherInfo,
    std::hash<uint64_t>, std::equal_to<uint64_t>,
    RebindAlloc<std::pair<const uint64_t, PublisherInfo>>>;

  PublisherMap publishers_;

  std::mutex runtime_mutex_;
};

RCLCPP_PUBLIC
IntraProcessManagerImplBase::SharedPtr
create_default_impl();

}  // namespace intra_process_manager
}  // namespace rclcpp

#endif  // RCLCPP__INTRA_PROCESS_MANAGER_IMPL_HPP_
<commit_msg>Replaced strncpy with memcpy (#684)<commit_after>// Copyright 2015 Open Source Robotics Foundation, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef RCLCPP__INTRA_PROCESS_MANAGER_IMPL_HPP_
#define RCLCPP__INTRA_PROCESS_MANAGER_IMPL_HPP_

#include <algorithm>
#include <array>
#include <atomic>
#include <cstring>
#include <functional>
#include <limits>
#include <map>
#include <memory>
#include <mutex>
#include <set>
#include <stdexcept>
#include <string>
#include <unordered_map>
#include <utility>

#include "rmw/validate_full_topic_name.h"

#include "rclcpp/macros.hpp"
#include "rclcpp/mapped_ring_buffer.hpp"
#include "rclcpp/publisher.hpp"
#include "rclcpp/subscription.hpp"
#include "rclcpp/visibility_control.hpp"

namespace rclcpp
{
namespace intra_process_manager
{

class IntraProcessManagerImplBase
{
public:
  RCLCPP_SMART_PTR_DEFINITIONS_NOT_COPYABLE(IntraProcessManagerImplBase)

  IntraProcessManagerImplBase() = default;
  virtual ~IntraProcessManagerImplBase() = default;

  virtual void
  add_subscription(uint64_t id, SubscriptionBase::SharedPtr subscription) = 0;

  virtual void
  remove_subscription(uint64_t intra_process_subscription_id) = 0;

  virtual void add_publisher(
    uint64_t id,
    PublisherBase::WeakPtr publisher,
    mapped_ring_buffer::MappedRingBufferBase::SharedPtr mrb,
    size_t size) = 0;

  virtual void
  remove_publisher(uint64_t intra_process_publisher_id) = 0;

  virtual mapped_ring_buffer::MappedRingBufferBase::SharedPtr
  get_publisher_info_for_id(
    uint64_t intra_process_publisher_id,
    uint64_t & message_seq) = 0;

  virtual void
  store_intra_process_message(uint64_t intra_process_publisher_id, uint64_t message_seq) = 0;

  virtual mapped_ring_buffer::MappedRingBufferBase::SharedPtr
  take_intra_process_message(
    uint64_t intra_process_publisher_id,
    uint64_t message_sequence_number,
    uint64_t requesting_subscriptions_intra_process_id,
    size_t & size) = 0;

  virtual bool
  matches_any_publishers(const rmw_gid_t * id) const = 0;

  virtual size_t
  get_subscription_count(uint64_t intra_process_publisher_id) const = 0;

private:
  RCLCPP_DISABLE_COPY(IntraProcessManagerImplBase)
};

template<typename Allocator = std::allocator<void>>
class IntraProcessManagerImpl : public IntraProcessManagerImplBase
{
public:
  IntraProcessManagerImpl() = default;
  ~IntraProcessManagerImpl() = default;

  void
  add_subscription(uint64_t id, SubscriptionBase::SharedPtr subscription)
  {
    subscriptions_[id] = subscription;
    subscription_ids_by_topic_[fixed_size_string(subscription->get_topic_name())].insert(id);
  }

  void
  remove_subscription(uint64_t intra_process_subscription_id)
  {
    subscriptions_.erase(intra_process_subscription_id);
    for (auto & pair : subscription_ids_by_topic_) {
      pair.second.erase(intra_process_subscription_id);
    }
    // Iterate over all publisher infos and all stored subscription id's and
    // remove references to this subscription's id.
    for (auto & publisher_pair : publishers_) {
      for (auto & sub_pair : publisher_pair.second.target_subscriptions_by_message_sequence) {
        sub_pair.second.erase(intra_process_subscription_id);
      }
    }
  }

  void add_publisher(
    uint64_t id,
    PublisherBase::WeakPtr publisher,
    mapped_ring_buffer::MappedRingBufferBase::SharedPtr mrb,
    size_t size)
  {
    publishers_[id].publisher = publisher;
    // As long as the size of the ring buffer is less than the max sequence number, we're safe.
    if (size > std::numeric_limits<uint64_t>::max()) {
      throw std::invalid_argument("the calculated buffer size is too large");
    }
    publishers_[id].sequence_number.store(0);

    publishers_[id].buffer = mrb;
    publishers_[id].target_subscriptions_by_message_sequence.reserve(size);
  }

  void
  remove_publisher(uint64_t intra_process_publisher_id)
  {
    publishers_.erase(intra_process_publisher_id);
  }

  // return message_seq and mrb
  mapped_ring_buffer::MappedRingBufferBase::SharedPtr
  get_publisher_info_for_id(
    uint64_t intra_process_publisher_id,
    uint64_t & message_seq)
  {
    std::lock_guard<std::mutex> lock(runtime_mutex_);
    auto it = publishers_.find(intra_process_publisher_id);
    if (it == publishers_.end()) {
      throw std::runtime_error("get_publisher_info_for_id called with invalid publisher id");
    }
    PublisherInfo & info = it->second;
    // Calculate the next message sequence number.
    message_seq = info.sequence_number.fetch_add(1);

    return info.buffer;
  }

  void
  store_intra_process_message(uint64_t intra_process_publisher_id, uint64_t message_seq)
  {
    std::lock_guard<std::mutex> lock(runtime_mutex_);
    auto it = publishers_.find(intra_process_publisher_id);
    if (it == publishers_.end()) {
      throw std::runtime_error("store_intra_process_message called with invalid publisher id");
    }
    PublisherInfo & info = it->second;
    auto publisher = info.publisher.lock();
    if (!publisher) {
      throw std::runtime_error("publisher has unexpectedly gone out of scope");
    }

    // Figure out what subscriptions should receive the message.
    auto & destined_subscriptions =
      subscription_ids_by_topic_[fixed_size_string(publisher->get_topic_name())];
    // Store the list for later comparison.
    if (info.target_subscriptions_by_message_sequence.count(message_seq) == 0) {
      info.target_subscriptions_by_message_sequence.emplace(
        message_seq, AllocSet(std::less<uint64_t>(), uint64_allocator));
    } else {
      info.target_subscriptions_by_message_sequence[message_seq].clear();
    }
    std::copy(
      destined_subscriptions.begin(), destined_subscriptions.end(),
      // Memory allocation occurs in info.target_subscriptions_by_message_sequence[message_seq]
      std::inserter(
        info.target_subscriptions_by_message_sequence[message_seq],
        // This ends up only being a hint to std::set, could also be .begin().
        info.target_subscriptions_by_message_sequence[message_seq].end()
      )
    );
  }

  mapped_ring_buffer::MappedRingBufferBase::SharedPtr
  take_intra_process_message(
    uint64_t intra_process_publisher_id,
    uint64_t message_sequence_number,
    uint64_t requesting_subscriptions_intra_process_id,
    size_t & size
  )
  {
    std::lock_guard<std::mutex> lock(runtime_mutex_);
    PublisherInfo * info;
    {
      auto it = publishers_.find(intra_process_publisher_id);
      if (it == publishers_.end()) {
        // Publisher is either invalid or no longer exists.
        return 0;
      }
      info = &it->second;
    }
    // Figure out how many subscriptions are left.
    AllocSet * target_subs;
    {
      auto it = info->target_subscriptions_by_message_sequence.find(message_sequence_number);
      if (it == info->target_subscriptions_by_message_sequence.end()) {
        // Message is no longer being stored by this publisher.
        return 0;
      }
      target_subs = &it->second;
    }
    {
      auto it = std::find(
        target_subs->begin(), target_subs->end(),
        requesting_subscriptions_intra_process_id);
      if (it == target_subs->end()) {
        // This publisher id/message seq pair was not intended for this subscription.
        return 0;
      }
      target_subs->erase(it);
    }
    size = target_subs->size();
    return info->buffer;
  }

  bool
  matches_any_publishers(const rmw_gid_t * id) const
  {
    for (auto & publisher_pair : publishers_) {
      auto publisher = publisher_pair.second.publisher.lock();
      if (!publisher) {
        continue;
      }
      if (*publisher.get() == id) {
        return true;
      }
    }
    return false;
  }

  size_t
  get_subscription_count(uint64_t intra_process_publisher_id) const
  {
    auto publisher_it = publishers_.find(intra_process_publisher_id);
    if (publisher_it == publishers_.end()) {
      // Publisher is either invalid or no longer exists.
      return 0;
    }
    auto publisher = publisher_it->second.publisher.lock();
    if (!publisher) {
      throw std::runtime_error("publisher has unexpectedly gone out of scope");
    }
    auto sub_map_it =
      subscription_ids_by_topic_.find(fixed_size_string(publisher->get_topic_name()));
    if (sub_map_it == subscription_ids_by_topic_.end()) {
      // No intraprocess subscribers
      return 0;
    }
    return sub_map_it->second.size();
  }

private:
  RCLCPP_DISABLE_COPY(IntraProcessManagerImpl)

  using FixedSizeString = std::array<char, RMW_TOPIC_MAX_NAME_LENGTH + 1>;

  FixedSizeString
  fixed_size_string(const char * str) const
  {
    FixedSizeString ret;
    size_t size = std::strlen(str) + 1;
    if (size > ret.size()) {
      throw std::runtime_error("failed to copy topic name");
    }
    std::memcpy(ret.data(), str, size);
    return ret;
  }
  struct strcmp_wrapper
  {
    bool
    operator()(const FixedSizeString lhs, const FixedSizeString rhs) const
    {
      return std::strcmp(lhs.data(), rhs.data()) < 0;
    }
  };

  template<typename T>
  using RebindAlloc = typename std::allocator_traits<Allocator>::template rebind_alloc<T>;

  RebindAlloc<uint64_t> uint64_allocator;

  using AllocSet = std::set<uint64_t, std::less<uint64_t>, RebindAlloc<uint64_t>>;
  using SubscriptionMap = std::unordered_map<
    uint64_t, SubscriptionBase::WeakPtr,
    std::hash<uint64_t>, std::equal_to<uint64_t>,
    RebindAlloc<std::pair<const uint64_t, SubscriptionBase::WeakPtr>>>;

  using IDTopicMap = std::map<
    FixedSizeString,
    AllocSet,
    strcmp_wrapper,
    RebindAlloc<std::pair<const FixedSizeString, AllocSet>>>;

  SubscriptionMap subscriptions_;

  IDTopicMap subscription_ids_by_topic_;

  struct PublisherInfo
  {
    RCLCPP_DISABLE_COPY(PublisherInfo)

    PublisherInfo() = default;

    PublisherBase::WeakPtr publisher;
    std::atomic<uint64_t> sequence_number;
    mapped_ring_buffer::MappedRingBufferBase::SharedPtr buffer;

    using TargetSubscriptionsMap = std::unordered_map<
      uint64_t, AllocSet,
      std::hash<uint64_t>, std::equal_to<uint64_t>,
      RebindAlloc<std::pair<const uint64_t, AllocSet>>>;
    TargetSubscriptionsMap target_subscriptions_by_message_sequence;
  };

  using PublisherMap = std::unordered_map<
    uint64_t, PublisherInfo,
    std::hash<uint64_t>, std::equal_to<uint64_t>,
    RebindAlloc<std::pair<const uint64_t, PublisherInfo>>>;

  PublisherMap publishers_;

  std::mutex runtime_mutex_;
};

RCLCPP_PUBLIC
IntraProcessManagerImplBase::SharedPtr
create_default_impl();

}  // namespace intra_process_manager
}  // namespace rclcpp

#endif  // RCLCPP__INTRA_PROCESS_MANAGER_IMPL_HPP_
<|endoftext|>