<commit_before>#include "PowerUp.hpp"

// Implementation headers
#include "Ghost.hpp"
#include "client.hpp"
#include "SoundManager.hpp"

static void setScared(Ghost*, std::map<GameSprite*, ActorMovement*> &,
 AnimationFilm*);
static void ScaredToNormal(Ghost *ghost,
 std::map<GameSprite*, ActorMovement*> &akmovs,
 AnimationFilm *film);

struct GhostRevertTaskData : public TaskData {
	_pcoca *pkoka;
}; // struct GhostRevertTaskData
struct GhostRevertTask : public Task {
	void operator()(TaskData *);
	Task &operator++(void);
	GhostRevertTask(timestamp_t time); ~GhostRevertTask(void);
}; // struct GhostRevertTask

void powerup_coca(Sprite *p, Sprite *stoocker, void *c) {
	if (p->IsVisible()) if (c) {
		_pcoca *pkoka = CAST(_pcoca*, c);
		p->SetVisibility(false); // powerup

		// Change ghosts' status, animation film and anim delay
		AnimationFilm *scared_film = pkoka->filmhold->
		 GetFilm("snailyeat");
		std::map<GameSprite*, ActorMovement*> &akmovs =
		 *pkoka->akmovs;
		setScared(pkoka->ghost->stalker, akmovs, scared_film);
		setScared(pkoka->ghost->random, akmovs, scared_film);
		setScared(pkoka->ghost->retard, akmovs, scared_film);
		setScared(pkoka->ghost->kieken, akmovs, scared_film);

		GhostRevertTaskData *grtd = new GhostRevertTaskData;
		grtd->pkoka = pkoka;
		pkoka->sch->_register(
		 new GhostRevertTask(getCurrentTime() + EATABLE_DURATION),
		 grtd);

		SoundManager::Singleton()->PlayEffect("./resources/sounds/scratch.wav");

	}
} // powerup_coca

static void setScared(Ghost *ghost,
 std::map<GameSprite*, ActorMovement*> &akmovs,
 AnimationFilm *film)
{
	if(ghost->GetState() == NORMAL) {
		ghost->SetState(SCARED);
		ghost->setFilm(film);
		akmovs[ghost]->setDelay(30);
	}
} // setScared

static void ScaredToNormal(Ghost *ghost,
 std::map<GameSprite*, ActorMovement*> &akmovs,
 AnimationFilm *film)
{
	if(ghost->GetState() == SCARED) {
		ghost->SetState(NORMAL);
		ghost->setFilm(film);
		akmovs[ghost]->setDelay(15);
	}
}

GhostRevertTask::GhostRevertTask(timestamp_t time) :
	Task(time, false) { }
GhostRevertTask::~GhostRevertTask(void) { }

void GhostRevertTask::operator()(TaskData *d) {
	_pcoca *pkoka = dynamic_cast<GhostRevertTaskData*>(d)->pkoka;

	// Change ghosts' status, animation film and anim delay
	AnimationFilm *_film = pkoka->filmhold->
	GetFilm("snaily");
	AnimationFilm *l_film = pkoka->filmhold->
	GetFilm("snailyl");
	AnimationFilm *d_film = pkoka->filmhold->
	GetFilm("snailyd");
	AnimationFilm *b_film = pkoka->filmhold->
	GetFilm("snailyb");

	std::map<GameSprite*, ActorMovement*> &akmovs =
		*pkoka->akmovs;
	ScaredToNormal(pkoka->ghost->stalker, akmovs, _film);
	ScaredToNormal(pkoka->ghost->random, akmovs, d_film);
	ScaredToNormal(pkoka->ghost->retard, akmovs, b_film);
	ScaredToNormal(pkoka->ghost->kieken, akmovs, l_film);
} // GhostRevertTask::()

Task &GhostRevertTask::operator++(void) { return *this; }

void Ghost_collision_callback(Sprite *ghost, Sprite *pacman, void *c) {
	Ghost* gs = dynamic_cast<Ghost*>(ghost);
	if(gs->GetState() == SCARED) { if (c) {
		// It gets called : D
		_gcoca *gkoka = CAST(_gcoca*, c);
		AnimationFilm *retreat_film = gkoka->filmhold->
		 GetFilm("snailyate");
		std::map<GameSprite*, ActorMovement*> &akmovs =
		 *gkoka->akmovs;

		gs->SetState(RETREAT);
		gs->setFilm(retreat_film);
		akmovs[gs]->setDelay(12);
		CollisionChecker::Singleton()->Cancel(gkoka->left_right, gs);
		CollisionChecker::Singleton()->Register(gkoka->down, gs);

		////testing TODO
		CollisionChecker::Singleton()->Cancel(gkoka->lair, gs);

	} else
		db("Warning: Useless pacman-ghost callback");
	}
} // collision_callback

void ghost_uneating_callback(Sprite *waypoint, Sprite *_ghost, void *c) {
	Ghost* gs = dynamic_cast<Ghost*>(_ghost);
	if(gs->GetState() == RETREAT) if(c){
		AnimationFilm *film;
		_gcoca *gkoka = CAST(_gcoca*, c);
		if(gs == gkoka->ghost->stalker)
			film = gkoka->filmhold->GetFilm("snaily");
		else if(gs == gkoka->ghost->random)
			film = gkoka->filmhold->GetFilm("snailyd");
		else if(gs == gkoka->ghost->retard)
			film = gkoka->filmhold->GetFilm("snailyb");
		else film = gkoka->filmhold->GetFilm("snailyl");
		gs->SetState(NORMAL);
		gs->setFilm(film);
		(*gkoka->akmovs)[gs]->setDelay(15);
		(*gkoka->akmovs)[gs]->pressed(ActorMovement::UP, getCurrentTime());
		CollisionChecker::Singleton()->Cancel(gkoka->down, gs);
		CollisionChecker::Singleton()->Register(gkoka->left_right, gs);
	}

} // ghost_uneating_callback
<commit_msg>Fixed pacman-eats-too-many-power-ups-too-soon bug<commit_after>#include "PowerUp.hpp"

// Implementation headers
#include "Ghost.hpp"
#include "client.hpp"
#include "SoundManager.hpp"

static void setScared(Ghost*, std::map<GameSprite*, ActorMovement*> &,
 AnimationFilm*);
static void ScaredToNormal(Ghost *ghost,
 std::map<GameSprite*, ActorMovement*> &akmovs,
 AnimationFilm *film);

struct GhostRevertTaskData : public TaskData {
	_pcoca *pkoka;
}; // struct GhostRevertTaskData
struct GhostRevertTask : public Task {
	void operator()(TaskData *);
	Task &operator++(void);
	GhostRevertTask(timestamp_t time); ~GhostRevertTask(void);
}; // struct GhostRevertTask

void powerup_coca(Sprite *p, Sprite *stoocker, void *c) {
	static Task *ghost_revert_task = CAST(Task*, 0);
	if (p->IsVisible()) if (c) {
		_pcoca *pkoka = CAST(_pcoca*, c);
		p->SetVisibility(false); // powerup

		// Change ghosts' status, animation film and anim delay
		AnimationFilm *scared_film = pkoka->filmhold->
		 GetFilm("snailyeat");
		std::map<GameSprite*, ActorMovement*> &akmovs =
		 *pkoka->akmovs;
		setScared(pkoka->ghost->stalker, akmovs, scared_film);
		setScared(pkoka->ghost->random, akmovs, scared_film);
		setScared(pkoka->ghost->retard, akmovs, scared_film);
		setScared(pkoka->ghost->kieken, akmovs, scared_film);

		if (ghost_revert_task)
			pkoka->sch->cancel(ghost_revert_task);
		GhostRevertTaskData *grtd = new GhostRevertTaskData;
		grtd->pkoka = pkoka;
		pkoka->sch->_register(ghost_revert_task =
		 new GhostRevertTask(getCurrentTime() + EATABLE_DURATION),
		 grtd);

		SoundManager::Singleton()->PlayEffect("./resources/sounds/scratch.wav");

	}
} // powerup_coca

static void setScared(Ghost *ghost,
 std::map<GameSprite*, ActorMovement*> &akmovs,
 AnimationFilm *film)
{
	if(ghost->GetState() == NORMAL) {
		ghost->SetState(SCARED);
		ghost->setFilm(film);
		akmovs[ghost]->setDelay(30);
	}
} // setScared

static void ScaredToNormal(Ghost *ghost,
 std::map<GameSprite*, ActorMovement*> &akmovs,
 AnimationFilm *film)
{
	if(ghost->GetState() == SCARED) {
		ghost->SetState(NORMAL);
		ghost->setFilm(film);
		akmovs[ghost]->setDelay(15);
	}
}

GhostRevertTask::GhostRevertTask(timestamp_t time) :
	Task(time, false) { }
GhostRevertTask::~GhostRevertTask(void) { }

void GhostRevertTask::operator()(TaskData *d) {
	_pcoca *pkoka = dynamic_cast<GhostRevertTaskData*>(d)->pkoka;

	// Change ghosts' status, animation film and anim delay
	AnimationFilm *_film = pkoka->filmhold->
	GetFilm("snaily");
	AnimationFilm *l_film = pkoka->filmhold->
	GetFilm("snailyl");
	AnimationFilm *d_film = pkoka->filmhold->
	GetFilm("snailyd");
	AnimationFilm *b_film = pkoka->filmhold->
	GetFilm("snailyb");

	std::map<GameSprite*, ActorMovement*> &akmovs =
		*pkoka->akmovs;
	ScaredToNormal(pkoka->ghost->stalker, akmovs, _film);
	ScaredToNormal(pkoka->ghost->random, akmovs, d_film);
	ScaredToNormal(pkoka->ghost->retard, akmovs, b_film);
	ScaredToNormal(pkoka->ghost->kieken, akmovs, l_film);
} // GhostRevertTask::()

Task &GhostRevertTask::operator++(void) { return *this; }

void Ghost_collision_callback(Sprite *ghost, Sprite *pacman, void *c) {
	Ghost* gs = dynamic_cast<Ghost*>(ghost);
	if(gs->GetState() == SCARED) { if (c) {
		// It gets called : D
		_gcoca *gkoka = CAST(_gcoca*, c);
		AnimationFilm *retreat_film = gkoka->filmhold->
		 GetFilm("snailyate");
		std::map<GameSprite*, ActorMovement*> &akmovs =
		 *gkoka->akmovs;

		gs->SetState(RETREAT);
		gs->setFilm(retreat_film);
		akmovs[gs]->setDelay(12);
		CollisionChecker::Singleton()->Cancel(gkoka->left_right, gs);
		CollisionChecker::Singleton()->Register(gkoka->down, gs);

		////testing TODO
		CollisionChecker::Singleton()->Cancel(gkoka->lair, gs);

	} else
		db("Warning: Useless pacman-ghost callback");
	}
} // collision_callback

void ghost_uneating_callback(Sprite *waypoint, Sprite *_ghost, void *c) {
	Ghost* gs = dynamic_cast<Ghost*>(_ghost);
	if(gs->GetState() == RETREAT) if(c){
		AnimationFilm *film;
		_gcoca *gkoka = CAST(_gcoca*, c);
		if(gs == gkoka->ghost->stalker)
			film = gkoka->filmhold->GetFilm("snaily");
		else if(gs == gkoka->ghost->random)
			film = gkoka->filmhold->GetFilm("snailyd");
		else if(gs == gkoka->ghost->retard)
			film = gkoka->filmhold->GetFilm("snailyb");
		else film = gkoka->filmhold->GetFilm("snailyl");
		gs->SetState(NORMAL);
		gs->setFilm(film);
		(*gkoka->akmovs)[gs]->setDelay(15);
		(*gkoka->akmovs)[gs]->pressed(ActorMovement::UP, getCurrentTime());
		CollisionChecker::Singleton()->Cancel(gkoka->down, gs);
		CollisionChecker::Singleton()->Register(gkoka->left_right, gs);
	}

} // ghost_uneating_callback
<|endoftext|>


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

#include "fog_volume.h"

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

void FogVolume::_bind_methods() {
	ClassDB::bind_method(D_METHOD("set_extents", "extents"), &FogVolume::set_extents);
	ClassDB::bind_method(D_METHOD("get_extents"), &FogVolume::get_extents);
	ClassDB::bind_method(D_METHOD("set_shape", "shape"), &FogVolume::set_shape);
	ClassDB::bind_method(D_METHOD("get_shape"), &FogVolume::get_shape);
	ClassDB::bind_method(D_METHOD("set_material", "material"), &FogVolume::set_material);
	ClassDB::bind_method(D_METHOD("get_material"), &FogVolume::get_material);

	ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "extents", PROPERTY_HINT_RANGE, "0.01,1024,0.01,or_greater"), "set_extents", "get_extents");
	ADD_PROPERTY(PropertyInfo(Variant::INT, "shape", PROPERTY_HINT_ENUM, "Ellipsoid,Box,World"), "set_shape", "get_shape");
	ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "material", PROPERTY_HINT_RESOURCE_TYPE, "FogMaterial,ShaderMaterial"), "set_material", "get_material");
}

void FogVolume::_validate_property(PropertyInfo &property) const {
	if (property.name == "extents" && shape == RS::FOG_VOLUME_SHAPE_WORLD) {
		property.usage = PROPERTY_USAGE_NONE;
		return;
	}
}

void FogVolume::set_extents(const Vector3 &p_extents) {
	extents = p_extents;
	extents.x = MAX(0.0, extents.x);
	extents.y = MAX(0.0, extents.y);
	extents.z = MAX(0.0, extents.z);
	RS::get_singleton()->fog_volume_set_extents(_get_volume(), extents);
	update_gizmos();
}

Vector3 FogVolume::get_extents() const {
	return extents;
}

void FogVolume::set_shape(RS::FogVolumeShape p_type) {
	shape = p_type;
	RS::get_singleton()->fog_volume_set_shape(_get_volume(), shape);
	RS::get_singleton()->instance_set_ignore_culling(get_instance(), shape == RS::FOG_VOLUME_SHAPE_WORLD);
	update_gizmos();
	notify_property_list_changed();
}

RS::FogVolumeShape FogVolume::get_shape() const {
	return shape;
}

void FogVolume::set_material(const Ref<Material> &p_material) {
	material = p_material;
	RID material_rid;
	if (material.is_valid()) {
		material_rid = material->get_rid();
	}
	RS::get_singleton()->fog_volume_set_material(_get_volume(), material_rid);
	update_gizmos();
}

Ref<Material> FogVolume::get_material() const {
	return material;
}

AABB FogVolume::get_aabb() const {
	if (shape != RS::FOG_VOLUME_SHAPE_WORLD) {
		return AABB(-extents, extents * 2);
	}
	return AABB();
}

TypedArray<String> FogVolume::get_configuration_warnings() const {
	TypedArray<String> warnings = Node::get_configuration_warnings();

	if (!get_viewport()->find_world_3d()->get_environment()->is_volumetric_fog_enabled()) {
		warnings.push_back(("Fog Volumes need volumetric fog to be enabled in the scene's Environment in order to be visible."));
	}

	return warnings;
}

FogVolume::FogVolume() {
	volume = RS::get_singleton()->fog_volume_create();
	RS::get_singleton()->fog_volume_set_shape(volume, RS::FOG_VOLUME_SHAPE_BOX);
	set_base(volume);
}

FogVolume::~FogVolume() {
	RS::get_singleton()->free(volume);
}
<commit_msg>Fix crashing of `FogVolume`s when `WorldEnvironment` was added<commit_after>/*************************************************************************/
/*  fog_volume.cpp                                                       */
/*************************************************************************/
/*                       This file is part of:                           */
/*                           GODOT ENGINE                                */
/*                      https://godotengine.org                          */
/*************************************************************************/
/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur.                 */
/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md).   */
/*                                                                       */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the       */
/* "Software"), to deal in the Software without restriction, including   */
/* without limitation the rights to use, copy, modify, merge, publish,   */
/* distribute, sublicense, and/or sell copies of the Software, and to    */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions:                                             */
/*                                                                       */
/* The above copyright notice and this permission notice shall be        */
/* included in all copies or substantial portions of the Software.       */
/*                                                                       */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY  */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,  */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE     */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */
/*************************************************************************/

#include "fog_volume.h"

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

void FogVolume::_bind_methods() {
	ClassDB::bind_method(D_METHOD("set_extents", "extents"), &FogVolume::set_extents);
	ClassDB::bind_method(D_METHOD("get_extents"), &FogVolume::get_extents);
	ClassDB::bind_method(D_METHOD("set_shape", "shape"), &FogVolume::set_shape);
	ClassDB::bind_method(D_METHOD("get_shape"), &FogVolume::get_shape);
	ClassDB::bind_method(D_METHOD("set_material", "material"), &FogVolume::set_material);
	ClassDB::bind_method(D_METHOD("get_material"), &FogVolume::get_material);

	ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "extents", PROPERTY_HINT_RANGE, "0.01,1024,0.01,or_greater"), "set_extents", "get_extents");
	ADD_PROPERTY(PropertyInfo(Variant::INT, "shape", PROPERTY_HINT_ENUM, "Ellipsoid,Box,World"), "set_shape", "get_shape");
	ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "material", PROPERTY_HINT_RESOURCE_TYPE, "FogMaterial,ShaderMaterial"), "set_material", "get_material");
}

void FogVolume::_validate_property(PropertyInfo &property) const {
	if (property.name == "extents" && shape == RS::FOG_VOLUME_SHAPE_WORLD) {
		property.usage = PROPERTY_USAGE_NONE;
		return;
	}
}

void FogVolume::set_extents(const Vector3 &p_extents) {
	extents = p_extents;
	extents.x = MAX(0.0, extents.x);
	extents.y = MAX(0.0, extents.y);
	extents.z = MAX(0.0, extents.z);
	RS::get_singleton()->fog_volume_set_extents(_get_volume(), extents);
	update_gizmos();
}

Vector3 FogVolume::get_extents() const {
	return extents;
}

void FogVolume::set_shape(RS::FogVolumeShape p_type) {
	shape = p_type;
	RS::get_singleton()->fog_volume_set_shape(_get_volume(), shape);
	RS::get_singleton()->instance_set_ignore_culling(get_instance(), shape == RS::FOG_VOLUME_SHAPE_WORLD);
	update_gizmos();
	notify_property_list_changed();
}

RS::FogVolumeShape FogVolume::get_shape() const {
	return shape;
}

void FogVolume::set_material(const Ref<Material> &p_material) {
	material = p_material;
	RID material_rid;
	if (material.is_valid()) {
		material_rid = material->get_rid();
	}
	RS::get_singleton()->fog_volume_set_material(_get_volume(), material_rid);
	update_gizmos();
}

Ref<Material> FogVolume::get_material() const {
	return material;
}

AABB FogVolume::get_aabb() const {
	if (shape != RS::FOG_VOLUME_SHAPE_WORLD) {
		return AABB(-extents, extents * 2);
	}
	return AABB();
}

TypedArray<String> FogVolume::get_configuration_warnings() const {
	TypedArray<String> warnings = Node::get_configuration_warnings();

	Ref<Environment> environment = get_viewport()->find_world_3d()->get_environment();

	if (environment.is_valid() && !environment->is_volumetric_fog_enabled()) {
		warnings.push_back(("Fog Volumes need volumetric fog to be enabled in the scene's Environment in order to be visible."));
	}

	return warnings;
}

FogVolume::FogVolume() {
	volume = RS::get_singleton()->fog_volume_create();
	RS::get_singleton()->fog_volume_set_shape(volume, RS::FOG_VOLUME_SHAPE_BOX);
	set_base(volume);
}

FogVolume::~FogVolume() {
	RS::get_singleton()->free(volume);
}
<|endoftext|>


<commit_before>// Copyright (c) 2020 ASMlover. All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
//
//  * Redistributions of source code must retain the above copyright
//    notice, this list ofconditions and the following disclaimer.
//
//  * Redistributions in binary form must reproduce the above copyright
//    notice, this list of conditions and the following disclaimer in
//    the documentation and/or other materialsprovided with the
//    distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
// COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
#include <stack>
#include <string_view>
#include "common.hh"
#include "harness.hh"

// Given a string containing just the characters '(', ')', '{', '}', '[' and ']',
// determine if the input string is valid.
//
// An input string is valid if:
//
// Open brackets must be closed by the same type of brackets.
// Open brackets must be closed in the correct order.
// Note that an empty string is also considered valid.
//
// Example 1:
//
// Input: "()"
// Output: true
// Example 2:
//
// Input: "()[]{}"
// Output: true
// Example 3:
//
// Input: "(]"
// Output: false
// Example 4:
//
// Input: "([)]"
// Output: false
// Example 5:
//
// Input: "{[]}"
// Output: true

class ValidParentheses final : public Singleton<ValidParentheses> {
  inline bool is_lparen(char c) const noexcept {
    return c == '(' || c == '[' || c == '{';
  }

  inline bool is_rparen(char c) const noexcept {
    return c == ')' || c == ']' || c == '}';
  }

  bool is_match(char c, char expected) const noexcept {
    switch (c) {
    case ')': return expected == '(';
    case ']': return expected == '[';
    case '}': return expected == '{';
    }
    return false;
  }

  bool is_valid(std::string_view s) const {
    std::stack<char> stack;
    for (char c : s) {
      if (is_lparen(c)) {
        stack.push(c);
      }
      else if (is_rparen(c)) {
        char top = stack.top();
        stack.pop();
        if (!is_match(c, top))
          return false;
      }
      else {
        return false;
      }
    }

    return stack.empty();
  }
public:
  void run() {
    HARNESS_TRUE(is_valid("()"));
    HARNESS_TRUE(is_valid("()[]{}"));
    HARNESS_TRUE(!is_valid("(]"));
    HARNESS_TRUE(!is_valid("([)]"));
    HARNESS_TRUE(is_valid("{[]}"));
  }
};

HARNESS_TEST(ValidParentheses, harness::FakeTester) {
  ValidParentheses::get_instance().run();
}
<commit_msg>:memo: docs(valid-parentheses): updated the documents for valid-parentheses<commit_after>// Copyright (c) 2020 ASMlover. All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
//
//  * Redistributions of source code must retain the above copyright
//    notice, this list ofconditions and the following disclaimer.
//
//  * Redistributions in binary form must reproduce the above copyright
//    notice, this list of conditions and the following disclaimer in
//    the documentation and/or other materialsprovided with the
//    distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
// COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
#include <stack>
#include <string_view>
#include "common.hh"
#include "harness.hh"

// [ADDR] https://leetcode.com/problems/valid-parentheses/description
//
// Given a string containing just the characters '(', ')', '{', '}', '[' and ']',
// determine if the input string is valid.
//
// An input string is valid if:
//
// Open brackets must be closed by the same type of brackets.
// Open brackets must be closed in the correct order.
// Note that an empty string is also considered valid.
//
// Example 1:
//
// Input: "()"
// Output: true
// Example 2:
//
// Input: "()[]{}"
// Output: true
// Example 3:
//
// Input: "(]"
// Output: false
// Example 4:
//
// Input: "([)]"
// Output: false
// Example 5:
//
// Input: "{[]}"
// Output: true

class ValidParentheses final : public Singleton<ValidParentheses> {
  inline bool is_lparen(char c) const noexcept {
    return c == '(' || c == '[' || c == '{';
  }

  inline bool is_rparen(char c) const noexcept {
    return c == ')' || c == ']' || c == '}';
  }

  bool is_match(char c, char expected) const noexcept {
    switch (c) {
    case ')': return expected == '(';
    case ']': return expected == '[';
    case '}': return expected == '{';
    }
    return false;
  }

  bool is_valid(std::string_view s) const {
    std::stack<char> stack;
    for (char c : s) {
      if (is_lparen(c)) {
        stack.push(c);
      }
      else if (is_rparen(c)) {
        char top = stack.top();
        stack.pop();
        if (!is_match(c, top))
          return false;
      }
      else {
        return false;
      }
    }

    return stack.empty();
  }
public:
  void run() {
    HARNESS_TRUE(is_valid("()"));
    HARNESS_TRUE(is_valid("()[]{}"));
    HARNESS_TRUE(!is_valid("(]"));
    HARNESS_TRUE(!is_valid("([)]"));
    HARNESS_TRUE(is_valid("{[]}"));
  }
};

HARNESS_TEST(ValidParentheses, harness::FakeTester) {
  ValidParentheses::get_instance().run();
}
<|endoftext|>


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

#include <boost/program_options.hpp>

#include <plasp/pddl/Description.h>
#include <plasp/sas/Description.h>
#include <plasp/sas/TranslatorASP.h>

int main(int argc, char **argv)
{
	namespace po = boost::program_options;

	po::options_description description("Allowed options");
	description.add_options()
		("help,h", "Display this help message.")
		("version,v", "Display version information.")
		("input,i", po::value<std::vector<std::string>>(), "Specify the SAS input file.")
		("format,f", po::value<std::string>()->default_value("SAS"), "Specify the file format (SAS or PDDL).");

	po::positional_options_description positionalOptionsDescription;
	positionalOptionsDescription.add("input", -1);

	po::variables_map variablesMap;

	const auto printHelp =
		[&]()
		{
			std::cout << "Usage: plasp file [options]" << std::endl;
			std::cout << "Translate PDDL instances to ASP facts." << std::endl << std::endl;

			std::cout << description;
		};

	try
	{
		po::store(po::command_line_parser(argc, argv)
			.options(description)
			.positional(positionalOptionsDescription)
			.run(),
			variablesMap);
		po::notify(variablesMap);
	}
	catch (const po::error &e)
	{
		std::cerr << "Error: " << e.what() << std::endl << std::endl;
		printHelp();
		return EXIT_FAILURE;
	}

	if (variablesMap.count("help"))
	{
		printHelp();
		return EXIT_SUCCESS;
	}

	if (variablesMap.count("version"))
	{
		std::cout << "plasp version 3.0.0" << std::endl;
		return EXIT_SUCCESS;
	}

	auto format = variablesMap["format"].as<std::string>();
	std::transform(format.begin(), format.end(), format.begin(), ::tolower);

	if (format == "sas")
	{
		if (variablesMap.count("input"))
		{
			const auto &inputFiles = variablesMap["input"].as<std::vector<std::string>>();

			if (inputFiles.size() > 1)
			{
				std::cerr << "Error: Only one input file allowed for SAS translation" << std::endl;
				printHelp();
				return EXIT_FAILURE;
			}

			const auto sasDescription = plasp::sas::Description::fromFile(inputFiles.front());
			const auto sasTranslator = plasp::sas::TranslatorASP(sasDescription);
			sasTranslator.translate(std::cout);
		}
		else
		{
			const auto sasDescription = plasp::sas::Description::fromStream(std::cin);
			const auto sasTranslator = plasp::sas::TranslatorASP(sasDescription);
			sasTranslator.translate(std::cout);
		}
	}
	else if (format == "pddl")
	{
		if (variablesMap.count("input"))
		{
			const auto &inputFiles = variablesMap["input"].as<std::vector<std::string>>();
			const auto pddlDescription = plasp::pddl::Description::fromFiles(inputFiles);
		}
		else
			const auto pddlDescription = plasp::pddl::Description::fromStream(std::cin);

		//std::cout << pddlDescription << std::endl;
	}

	return EXIT_SUCCESS;
}
<commit_msg>Removed now unnecessary check for number of files with SAS parser.<commit_after>#include <algorithm>
#include <iostream>
#include <string>

#include <boost/program_options.hpp>

#include <plasp/pddl/Description.h>
#include <plasp/sas/Description.h>
#include <plasp/sas/TranslatorASP.h>

int main(int argc, char **argv)
{
	namespace po = boost::program_options;

	po::options_description description("Allowed options");
	description.add_options()
		("help,h", "Display this help message.")
		("version,v", "Display version information.")
		("input,i", po::value<std::vector<std::string>>(), "Specify the SAS input file.")
		("format,f", po::value<std::string>()->default_value("SAS"), "Specify the file format (SAS or PDDL).");

	po::positional_options_description positionalOptionsDescription;
	positionalOptionsDescription.add("input", -1);

	po::variables_map variablesMap;

	const auto printHelp =
		[&]()
		{
			std::cout << "Usage: plasp file [options]" << std::endl;
			std::cout << "Translate PDDL instances to ASP facts." << std::endl << std::endl;

			std::cout << description;
		};

	try
	{
		po::store(po::command_line_parser(argc, argv)
			.options(description)
			.positional(positionalOptionsDescription)
			.run(),
			variablesMap);
		po::notify(variablesMap);
	}
	catch (const po::error &e)
	{
		std::cerr << "Error: " << e.what() << std::endl << std::endl;
		printHelp();
		return EXIT_FAILURE;
	}

	if (variablesMap.count("help"))
	{
		printHelp();
		return EXIT_SUCCESS;
	}

	if (variablesMap.count("version"))
	{
		std::cout << "plasp version 3.0.0" << std::endl;
		return EXIT_SUCCESS;
	}

	auto format = variablesMap["format"].as<std::string>();
	std::transform(format.begin(), format.end(), format.begin(), ::tolower);

	if (format == "sas")
	{
		if (variablesMap.count("input"))
		{
			const auto &inputFiles = variablesMap["input"].as<std::vector<std::string>>();
			const auto sasDescription = plasp::sas::Description::fromFile(inputFiles.front());
			const auto sasTranslator = plasp::sas::TranslatorASP(sasDescription);
			sasTranslator.translate(std::cout);
		}
		else
		{
			const auto sasDescription = plasp::sas::Description::fromStream(std::cin);
			const auto sasTranslator = plasp::sas::TranslatorASP(sasDescription);
			sasTranslator.translate(std::cout);
		}
	}
	else if (format == "pddl")
	{
		if (variablesMap.count("input"))
		{
			const auto &inputFiles = variablesMap["input"].as<std::vector<std::string>>();
			const auto pddlDescription = plasp::pddl::Description::fromFiles(inputFiles);
		}
		else
			const auto pddlDescription = plasp::pddl::Description::fromStream(std::cin);

		//std::cout << pddlDescription << std::endl;
	}

	return EXIT_SUCCESS;
}
<|endoftext|>


<commit_before>/** \file    create_literary_remains_records.cc
 *  \author  Dr. Johannes Ruscheinski
 *
 *  A tool for creating literary remains MARC records from Beacon files.
 */

/*
    Copyright (C) 2019-2020, Library of the University of Tübingen

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

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Affero General Public License for more details.

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

#include <unordered_map>
#include <unordered_set>
#include "FileUtil.h"
#include "MARC.h"
#include "RegexMatcher.h"
#include "StringUtil.h"
#include "TimeUtil.h"
#include "util.h"

namespace {

struct TitleRecordCounter {
    unsigned total_count_;
    unsigned religious_studies_count_;
public:
    TitleRecordCounter(): total_count_(0), religious_studies_count_(0) { }
    TitleRecordCounter(const TitleRecordCounter &other) = default;
    TitleRecordCounter(const bool is_relevant_to_reigious_studies)
        : total_count_(1), religious_studies_count_(is_relevant_to_reigious_studies ? 1 : 0) { }
    inline bool exceedsReligiousStudiesThreshold() const
        { return 100.0 * static_cast<double>(religious_studies_count_) / static_cast<double>(total_count_) >= 10.0 /* percent */; }
};

// Counts the number of religious studies title records for authors.
void CopyMarcAndCollectRelgiousStudiesFrequencies(
    MARC::Reader * const reader, MARC::Writer * const writer,
    std::unordered_map<std::string, TitleRecordCounter> * const author_ppn_to_relstudies_title_counters)
{
    while (auto record = reader->read()) {
        const bool rel_tag_found(record.findTag("REL") != record.end());

        for (const auto &author_name_and_author_ppn : record.getAllAuthorsAndPPNs()) {
            auto author_ppn_and_counter(author_ppn_to_relstudies_title_counters->find(author_name_and_author_ppn.second));
            if (author_ppn_and_counter == author_ppn_to_relstudies_title_counters->end())
                (*author_ppn_to_relstudies_title_counters)[author_name_and_author_ppn.second] = TitleRecordCounter(rel_tag_found);
            else {
                ++(author_ppn_and_counter->second.total_count_);
                if (rel_tag_found)
                    ++(author_ppn_and_counter->second.religious_studies_count_);
            }
        }

        writer->write(record);
    }
}

struct LiteraryRemainsInfo {
    std::string author_name_;
    std::string url_;
    std::string source_name_;
    std::string dates_;
public:
    LiteraryRemainsInfo() = default;
    LiteraryRemainsInfo(const LiteraryRemainsInfo &other) = default;
    LiteraryRemainsInfo(const std::string &author_name, const std::string &url, const std::string &source_name, const std::string &dates)
        : author_name_(author_name), url_(url), source_name_(source_name), dates_(dates) { }

    LiteraryRemainsInfo &operator=(const LiteraryRemainsInfo &rhs) = default;
};

std::vector<std::pair<RegexMatcher *, std::string>> CompileMatchers() {
    // Please note that the order in the following vector matters.  The first successful match will be used.
    const std::vector<std::pair<std::string, std::string>> patterns_and_replacements {
        { "v([0-9]+) ?- ?v([0-9]+)", "\\1 v. Chr. - \\2 v. Chr." },
        { "v([0-9]+)"              , "\\1 v. Chr."               },
    };

    std::vector<std::pair<RegexMatcher *, std::string>> compiled_patterns_and_replacements;
    for (const auto &pattern_and_replacement : patterns_and_replacements)
        compiled_patterns_and_replacements.push_back(
            std::pair<RegexMatcher *, std::string>(RegexMatcher::RegexMatcherFactoryOrDie(pattern_and_replacement.first),
                                                   pattern_and_replacement.second));
    return compiled_patterns_and_replacements;
}

const std::vector<std::pair<RegexMatcher *, std::string>> matchers_and_replacements(CompileMatchers());

std::string ReplaceNonStandardBCEDates(const std::string &dates) {
    for (const auto &matcher_and_replacement : matchers_and_replacements) {
            RegexMatcher * const matcher(matcher_and_replacement.first);
            const std::string replaced_string(matcher->replaceWithBackreferences(dates, matcher_and_replacement.second));
            if (replaced_string != dates)
                return replaced_string;
    }

    return dates;
}

void LoadAuthorGNDNumbersAndTagAuthors(
    MARC::Reader * const reader, MARC::Writer * const writer,
    const std::unordered_map<std::string, TitleRecordCounter> &author_ppn_to_relstudies_titles_counters,
    std::unordered_map<std::string, std::vector<LiteraryRemainsInfo>> * const gnd_numbers_to_literary_remains_infos_map,
    std::unordered_map<std::string, std::string> * const gnd_numbers_to_ppns_map)
{
    unsigned total_count(0), references_count(0), tagged_count(0);
    while (auto record = reader->read()) {
        ++total_count;

        auto beacon_field(record.findTag("BEA"));
        if (beacon_field == record.end())
            continue;

        const auto _100_field(record.findTag("100"));
        if (_100_field == record.end() or not _100_field->hasSubfield('a'))
            continue;

        std::string gnd_number;
        if (not MARC::GetGNDCode(record, &gnd_number))
            continue;

        (*gnd_numbers_to_ppns_map)[gnd_number] = record.getControlNumber();

        const auto numeration(_100_field->getFirstSubfieldWithCode('b'));
        const auto titles_and_other_words_associated_with_a_name(_100_field->getFirstSubfieldWithCode('c'));
        const auto name_and_numeration(_100_field->getFirstSubfieldWithCode('a') + (numeration.empty() ? "" : " " + numeration));
        const auto author_name(not titles_and_other_words_associated_with_a_name.empty()
                                   ? name_and_numeration + " (" + titles_and_other_words_associated_with_a_name + ")"
                                   : name_and_numeration);

        const auto dates(ReplaceNonStandardBCEDates(_100_field->getFirstSubfieldWithCode('d')));

        std::vector<LiteraryRemainsInfo> literary_remains_infos;
        while (beacon_field != record.end() and beacon_field->getTag() == "BEA") {
            literary_remains_infos.emplace_back(author_name, beacon_field->getFirstSubfieldWithCode('u'),
                                                beacon_field->getFirstSubfieldWithCode('a'), dates);
            ++beacon_field;
        }
        (*gnd_numbers_to_literary_remains_infos_map)[gnd_number] = literary_remains_infos;
        references_count += literary_remains_infos.size();

        if (author_ppn_to_relstudies_titles_counters.find(record.getControlNumber()) != author_ppn_to_relstudies_titles_counters.cend()) {
            record.insertField("REL", { { 'a', "1" }, { 'o', FileUtil::GetBasename(::progname) } });
            ++tagged_count;
        }

        writer->write(record);
    }

    LOG_INFO("Loaded " + std::to_string(references_count) + " literary remains references from \"" + reader->getPath()
             + "\" which contained a total of " + std::to_string(total_count) + " records.");
    LOG_INFO("Tagged " + std::to_string(tagged_count) + " authority records as relevant to religious studies.");
}

std::string NormaliseAuthorName(std::string author_name) {
    const auto comma_pos(author_name.find(','));
    if (comma_pos == std::string::npos)
        return author_name;

    std::string auxillary_info;
    const auto open_paren_pos(author_name.find('('));
    if (open_paren_pos != std::string::npos) {
        if (comma_pos > open_paren_pos)
            return author_name;

        auxillary_info = " " + author_name.substr(open_paren_pos);
        author_name.resize(open_paren_pos);
    }

    return StringUtil::TrimWhite(author_name.substr(comma_pos + 1)) + " " + StringUtil::TrimWhite(author_name.substr(0, comma_pos))
           + auxillary_info;
}

void AppendLiteraryRemainsRecords(
    MARC::Writer * const writer,
    const std::unordered_map<std::string, std::vector<LiteraryRemainsInfo>> &gnd_numbers_to_literary_remains_infos_map,
    const std::unordered_map<std::string, std::string> &gnd_numbers_to_ppns_map,
    const std::unordered_map<std::string, TitleRecordCounter> &author_ppn_to_relstudies_titles_counters)
{
    unsigned creation_count(0);
    for (const auto &gnd_numbers_and_literary_remains_infos : gnd_numbers_to_literary_remains_infos_map) {
        MARC::Record new_record(MARC::Record::TypeOfRecord::MIXED_MATERIALS, MARC::Record::BibliographicLevel::COLLECTION,
                                "LR" + gnd_numbers_and_literary_remains_infos.first);
        const std::string &author_name(gnd_numbers_and_literary_remains_infos.second.front().author_name_);
        std::string dates(gnd_numbers_and_literary_remains_infos.second.front().dates_.empty()
                              ? "" : " " + gnd_numbers_and_literary_remains_infos.second.front().dates_);
        new_record.insertField("003", "PipeLineGenerated");
        new_record.insertField("005", TimeUtil::GetCurrentDateAndTime("%Y%m%d%H%M%S") + ".0");
        new_record.insertField("008", "190606s2019    xx |||||      00| ||ger c");
        if (gnd_numbers_and_literary_remains_infos.second.front().dates_.empty())
            new_record.insertField("100", { { 'a', author_name }, { '0', "(DE-588)" + gnd_numbers_and_literary_remains_infos.first } });
        else
            new_record.insertField("100",
                                   { { 'a', author_name }, { '0', "(DE-588)" + gnd_numbers_and_literary_remains_infos.first },
                                     { 'd', gnd_numbers_and_literary_remains_infos.second.front().dates_ } });
        new_record.insertField("245", { { 'a', "Nachlass von " + NormaliseAuthorName(author_name) + dates } });

        for (const auto &literary_remains_info : gnd_numbers_and_literary_remains_infos.second)
            new_record.insertField("856",
                                   { { 'u', literary_remains_info.url_ },
                                       { '3', "Nachlassdatenbank (" + literary_remains_info.source_name_ + ")" } });

        // Do we have a religious studies author?
        const auto gnd_number_and_author_ppn(gnd_numbers_to_ppns_map.find(gnd_numbers_and_literary_remains_infos.first));
        if (unlikely(gnd_number_and_author_ppn == gnd_numbers_to_ppns_map.cend()))
            LOG_ERROR("we should *always* find the GND number in gnd_numbers_to_ppns_map!");
        const auto author_ppn_and_relstudies_titles_counter(
            author_ppn_to_relstudies_titles_counters.find(gnd_number_and_author_ppn->second));
        if (author_ppn_and_relstudies_titles_counter != author_ppn_to_relstudies_titles_counters.cend()
            and author_ppn_and_relstudies_titles_counter->second.exceedsReligiousStudiesThreshold())
            new_record.insertField("REL", { { 'a', "1" }, { 'o', FileUtil::GetBasename(::progname) } });

        writer->write(new_record);
    }

    LOG_INFO("Appended a total of " + std::to_string(creation_count) + " record(s).");
}

} // unnamed namespace

int Main(int argc, char **argv) {
    if (argc != 5)
        ::Usage("marc_input marc_output authority_records_input authority_records_output");

    auto title_reader(MARC::Reader::Factory(argv[1]));
    auto title_writer(MARC::Writer::Factory(argv[2]));
    std::unordered_map<std::string, TitleRecordCounter> author_ppn_to_relstudies_titles_counters;
    CopyMarcAndCollectRelgiousStudiesFrequencies(title_reader.get(), title_writer.get(), &author_ppn_to_relstudies_titles_counters);
    if (author_ppn_to_relstudies_titles_counters.empty())
        LOG_ERROR("You must run this program on an input that contains REL records!");

    auto authority_reader(MARC::Reader::Factory(argv[3]));
    auto authority_writer(MARC::Writer::Factory(argv[4]));
    std::unordered_map<std::string, std::vector<LiteraryRemainsInfo>> gnd_numbers_to_literary_remains_infos_map;
    std::unordered_map<std::string, std::string> gnd_numbers_to_ppns_map;
    LoadAuthorGNDNumbersAndTagAuthors(authority_reader.get(), authority_writer.get(), author_ppn_to_relstudies_titles_counters,
                                      &gnd_numbers_to_literary_remains_infos_map, &gnd_numbers_to_ppns_map);
    AppendLiteraryRemainsRecords(title_writer.get(), gnd_numbers_to_literary_remains_infos_map, gnd_numbers_to_ppns_map,
                                 author_ppn_to_relstudies_titles_counters);

    return EXIT_SUCCESS;
}
<commit_msg>Fixed indentation.<commit_after>/** \file    create_literary_remains_records.cc
 *  \author  Dr. Johannes Ruscheinski
 *
 *  A tool for creating literary remains MARC records from Beacon files.
 */

/*
    Copyright (C) 2019-2020, Library of the University of Tübingen

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

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Affero General Public License for more details.

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

#include <unordered_map>
#include <unordered_set>
#include "FileUtil.h"
#include "MARC.h"
#include "RegexMatcher.h"
#include "StringUtil.h"
#include "TimeUtil.h"
#include "util.h"

namespace {

struct TitleRecordCounter {
    unsigned total_count_;
    unsigned religious_studies_count_;
public:
    TitleRecordCounter(): total_count_(0), religious_studies_count_(0) { }
    TitleRecordCounter(const TitleRecordCounter &other) = default;
    TitleRecordCounter(const bool is_relevant_to_reigious_studies)
        : total_count_(1), religious_studies_count_(is_relevant_to_reigious_studies ? 1 : 0) { }
    inline bool exceedsReligiousStudiesThreshold() const
        { return 100.0 * static_cast<double>(religious_studies_count_) / static_cast<double>(total_count_) >= 10.0 /* percent */; }
};

// Counts the number of religious studies title records for authors.
void CopyMarcAndCollectRelgiousStudiesFrequencies(
    MARC::Reader * const reader, MARC::Writer * const writer,
    std::unordered_map<std::string, TitleRecordCounter> * const author_ppn_to_relstudies_title_counters)
{
    while (auto record = reader->read()) {
        const bool rel_tag_found(record.findTag("REL") != record.end());

        for (const auto &author_name_and_author_ppn : record.getAllAuthorsAndPPNs()) {
            auto author_ppn_and_counter(author_ppn_to_relstudies_title_counters->find(author_name_and_author_ppn.second));
            if (author_ppn_and_counter == author_ppn_to_relstudies_title_counters->end())
                (*author_ppn_to_relstudies_title_counters)[author_name_and_author_ppn.second] = TitleRecordCounter(rel_tag_found);
            else {
                ++(author_ppn_and_counter->second.total_count_);
                if (rel_tag_found)
                    ++(author_ppn_and_counter->second.religious_studies_count_);
            }
        }

        writer->write(record);
    }
}

struct LiteraryRemainsInfo {
    std::string author_name_;
    std::string url_;
    std::string source_name_;
    std::string dates_;
public:
    LiteraryRemainsInfo() = default;
    LiteraryRemainsInfo(const LiteraryRemainsInfo &other) = default;
    LiteraryRemainsInfo(const std::string &author_name, const std::string &url, const std::string &source_name, const std::string &dates)
        : author_name_(author_name), url_(url), source_name_(source_name), dates_(dates) { }

    LiteraryRemainsInfo &operator=(const LiteraryRemainsInfo &rhs) = default;
};

std::vector<std::pair<RegexMatcher *, std::string>> CompileMatchers() {
    // Please note that the order in the following vector matters.  The first successful match will be used.
    const std::vector<std::pair<std::string, std::string>> patterns_and_replacements {
        { "v([0-9]+) ?- ?v([0-9]+)", "\\1 v. Chr. - \\2 v. Chr." },
        { "v([0-9]+)"              , "\\1 v. Chr."               },
    };

    std::vector<std::pair<RegexMatcher *, std::string>> compiled_patterns_and_replacements;
    for (const auto &pattern_and_replacement : patterns_and_replacements)
        compiled_patterns_and_replacements.push_back(
            std::pair<RegexMatcher *, std::string>(RegexMatcher::RegexMatcherFactoryOrDie(pattern_and_replacement.first),
                                                   pattern_and_replacement.second));
    return compiled_patterns_and_replacements;
}

const std::vector<std::pair<RegexMatcher *, std::string>> matchers_and_replacements(CompileMatchers());

std::string ReplaceNonStandardBCEDates(const std::string &dates) {
    for (const auto &matcher_and_replacement : matchers_and_replacements) {
        RegexMatcher * const matcher(matcher_and_replacement.first);
        const std::string replaced_string(matcher->replaceWithBackreferences(dates, matcher_and_replacement.second));
        if (replaced_string != dates)
            return replaced_string;
    }

    return dates;
}

void LoadAuthorGNDNumbersAndTagAuthors(
    MARC::Reader * const reader, MARC::Writer * const writer,
    const std::unordered_map<std::string, TitleRecordCounter> &author_ppn_to_relstudies_titles_counters,
    std::unordered_map<std::string, std::vector<LiteraryRemainsInfo>> * const gnd_numbers_to_literary_remains_infos_map,
    std::unordered_map<std::string, std::string> * const gnd_numbers_to_ppns_map)
{
    unsigned total_count(0), references_count(0), tagged_count(0);
    while (auto record = reader->read()) {
        ++total_count;

        auto beacon_field(record.findTag("BEA"));
        if (beacon_field == record.end())
            continue;

        const auto _100_field(record.findTag("100"));
        if (_100_field == record.end() or not _100_field->hasSubfield('a'))
            continue;

        std::string gnd_number;
        if (not MARC::GetGNDCode(record, &gnd_number))
            continue;

        (*gnd_numbers_to_ppns_map)[gnd_number] = record.getControlNumber();

        const auto numeration(_100_field->getFirstSubfieldWithCode('b'));
        const auto titles_and_other_words_associated_with_a_name(_100_field->getFirstSubfieldWithCode('c'));
        const auto name_and_numeration(_100_field->getFirstSubfieldWithCode('a') + (numeration.empty() ? "" : " " + numeration));
        const auto author_name(not titles_and_other_words_associated_with_a_name.empty()
                                   ? name_and_numeration + " (" + titles_and_other_words_associated_with_a_name + ")"
                                   : name_and_numeration);

        const auto dates(ReplaceNonStandardBCEDates(_100_field->getFirstSubfieldWithCode('d')));

        std::vector<LiteraryRemainsInfo> literary_remains_infos;
        while (beacon_field != record.end() and beacon_field->getTag() == "BEA") {
            literary_remains_infos.emplace_back(author_name, beacon_field->getFirstSubfieldWithCode('u'),
                                                beacon_field->getFirstSubfieldWithCode('a'), dates);
            ++beacon_field;
        }
        (*gnd_numbers_to_literary_remains_infos_map)[gnd_number] = literary_remains_infos;
        references_count += literary_remains_infos.size();

        if (author_ppn_to_relstudies_titles_counters.find(record.getControlNumber()) != author_ppn_to_relstudies_titles_counters.cend()) {
            record.insertField("REL", { { 'a', "1" }, { 'o', FileUtil::GetBasename(::progname) } });
            ++tagged_count;
        }

        writer->write(record);
    }

    LOG_INFO("Loaded " + std::to_string(references_count) + " literary remains references from \"" + reader->getPath()
             + "\" which contained a total of " + std::to_string(total_count) + " records.");
    LOG_INFO("Tagged " + std::to_string(tagged_count) + " authority records as relevant to religious studies.");
}

std::string NormaliseAuthorName(std::string author_name) {
    const auto comma_pos(author_name.find(','));
    if (comma_pos == std::string::npos)
        return author_name;

    std::string auxillary_info;
    const auto open_paren_pos(author_name.find('('));
    if (open_paren_pos != std::string::npos) {
        if (comma_pos > open_paren_pos)
            return author_name;

        auxillary_info = " " + author_name.substr(open_paren_pos);
        author_name.resize(open_paren_pos);
    }

    return StringUtil::TrimWhite(author_name.substr(comma_pos + 1)) + " " + StringUtil::TrimWhite(author_name.substr(0, comma_pos))
           + auxillary_info;
}

void AppendLiteraryRemainsRecords(
    MARC::Writer * const writer,
    const std::unordered_map<std::string, std::vector<LiteraryRemainsInfo>> &gnd_numbers_to_literary_remains_infos_map,
    const std::unordered_map<std::string, std::string> &gnd_numbers_to_ppns_map,
    const std::unordered_map<std::string, TitleRecordCounter> &author_ppn_to_relstudies_titles_counters)
{
    unsigned creation_count(0);
    for (const auto &gnd_numbers_and_literary_remains_infos : gnd_numbers_to_literary_remains_infos_map) {
        MARC::Record new_record(MARC::Record::TypeOfRecord::MIXED_MATERIALS, MARC::Record::BibliographicLevel::COLLECTION,
                                "LR" + gnd_numbers_and_literary_remains_infos.first);
        const std::string &author_name(gnd_numbers_and_literary_remains_infos.second.front().author_name_);
        std::string dates(gnd_numbers_and_literary_remains_infos.second.front().dates_.empty()
                              ? "" : " " + gnd_numbers_and_literary_remains_infos.second.front().dates_);
        new_record.insertField("003", "PipeLineGenerated");
        new_record.insertField("005", TimeUtil::GetCurrentDateAndTime("%Y%m%d%H%M%S") + ".0");
        new_record.insertField("008", "190606s2019    xx |||||      00| ||ger c");
        if (gnd_numbers_and_literary_remains_infos.second.front().dates_.empty())
            new_record.insertField("100", { { 'a', author_name }, { '0', "(DE-588)" + gnd_numbers_and_literary_remains_infos.first } });
        else
            new_record.insertField("100",
                                   { { 'a', author_name }, { '0', "(DE-588)" + gnd_numbers_and_literary_remains_infos.first },
                                     { 'd', gnd_numbers_and_literary_remains_infos.second.front().dates_ } });
        new_record.insertField("245", { { 'a', "Nachlass von " + NormaliseAuthorName(author_name) + dates } });

        for (const auto &literary_remains_info : gnd_numbers_and_literary_remains_infos.second)
            new_record.insertField("856",
                                   { { 'u', literary_remains_info.url_ },
                                       { '3', "Nachlassdatenbank (" + literary_remains_info.source_name_ + ")" } });

        // Do we have a religious studies author?
        const auto gnd_number_and_author_ppn(gnd_numbers_to_ppns_map.find(gnd_numbers_and_literary_remains_infos.first));
        if (unlikely(gnd_number_and_author_ppn == gnd_numbers_to_ppns_map.cend()))
            LOG_ERROR("we should *always* find the GND number in gnd_numbers_to_ppns_map!");
        const auto author_ppn_and_relstudies_titles_counter(
            author_ppn_to_relstudies_titles_counters.find(gnd_number_and_author_ppn->second));
        if (author_ppn_and_relstudies_titles_counter != author_ppn_to_relstudies_titles_counters.cend()
            and author_ppn_and_relstudies_titles_counter->second.exceedsReligiousStudiesThreshold())
            new_record.insertField("REL", { { 'a', "1" }, { 'o', FileUtil::GetBasename(::progname) } });

        writer->write(new_record);
    }

    LOG_INFO("Appended a total of " + std::to_string(creation_count) + " record(s).");
}

} // unnamed namespace

int Main(int argc, char **argv) {
    if (argc != 5)
        ::Usage("marc_input marc_output authority_records_input authority_records_output");

    auto title_reader(MARC::Reader::Factory(argv[1]));
    auto title_writer(MARC::Writer::Factory(argv[2]));
    std::unordered_map<std::string, TitleRecordCounter> author_ppn_to_relstudies_titles_counters;
    CopyMarcAndCollectRelgiousStudiesFrequencies(title_reader.get(), title_writer.get(), &author_ppn_to_relstudies_titles_counters);
    if (author_ppn_to_relstudies_titles_counters.empty())
        LOG_ERROR("You must run this program on an input that contains REL records!");

    auto authority_reader(MARC::Reader::Factory(argv[3]));
    auto authority_writer(MARC::Writer::Factory(argv[4]));
    std::unordered_map<std::string, std::vector<LiteraryRemainsInfo>> gnd_numbers_to_literary_remains_infos_map;
    std::unordered_map<std::string, std::string> gnd_numbers_to_ppns_map;
    LoadAuthorGNDNumbersAndTagAuthors(authority_reader.get(), authority_writer.get(), author_ppn_to_relstudies_titles_counters,
                                      &gnd_numbers_to_literary_remains_infos_map, &gnd_numbers_to_ppns_map);
    AppendLiteraryRemainsRecords(title_writer.get(), gnd_numbers_to_literary_remains_infos_map, gnd_numbers_to_ppns_map,
                                 author_ppn_to_relstudies_titles_counters);

    return EXIT_SUCCESS;
}
<|endoftext|>


<commit_before>/*
**  Copyright (C) 2013 Aldebaran Robotics
**  See COPYING for the license
*/

#include <list>
#include <iostream>

#include <gtest/gtest.h>

#include <qi/qi.hpp>
#include <qi/application.hpp>
#include <qi/eventloop.hpp>
#include <qitype/anyobject.hpp>
#include <qitype/objecttypebuilder.hpp>
#include <qitype/proxysignal.hpp>
#include <qitype/proxyproperty.hpp>
#include <qimessaging/session.hpp>
#include <testsession/testsessionpair.hpp>

#define PERSIST_CHECK(code, cond, what, msdelay)  \
do                                           \
{                                            \
  code;                                      \
  for(unsigned i=0; i<50 && !(cond); ++i)    \
  {                                          \
    qi::os::msleep(1 + msdelay / 50);        \
    code;                                    \
  }                                          \
  what(cond);                                \
} while(0)

#define PERSIST_ASSERT(code, cond, msdelay)  \
 PERSIST_CHECK(code, cond, ASSERT_TRUE, msdelay)

#define PERSIST_EXPECT(code, cond, msdelay)  \
 PERSIST_CHECK(code, cond, EXPECT_TRUE, msdelay)

//cast to void to avoid unused warning.
#define PERSIST(code, cond, msdelay)  \
  PERSIST_CHECK(code, cond, (void),msdelay)

qiLogCategory("test");

class Foo
{
public:
  Foo() : _count1(0), _count2(0) {}
  void subscribe1();
  void subscribe2();
  void unsubscribe1();
  void unsubscribe2();
  int count1() { return _count1;}
  int count2() { return _count2;}
  void on1(int a, int b) { _count1 += a+b;}
  void on2() { ++_count2;}
  qi::Signal<int, int> sig1;
  qi::Signal<> sig2;

  int _count1, _count2;
  qi::SignalLink _l1, _l2;
};

void Foo::subscribe1()
{
  _l1 = sig1.connect(boost::bind(&Foo::on1, this, _1, _2));
}
void Foo::unsubscribe1()
{
  sig1.disconnect(_l1);
}
void Foo::subscribe2()
{
  _l2 = sig2.connect(boost::bind(&Foo::on2, this));
}
void Foo::unsubscribe2()
{
  sig2.disconnect(_l2);
}

QI_REGISTER_OBJECT(Foo, subscribe1, subscribe2, unsubscribe1, unsubscribe2,
  count1, count2, sig1, sig2);

TEST(Proxy, Signal)
{
  boost::shared_ptr<Foo> foo(new Foo);
  qi::AnyObject gfoo = qi::AnyReference::from(foo).toObject();
  ASSERT_TRUE(!!gfoo);
  qi::details::printMetaObject(std::cerr, gfoo.metaObject());
  // The session must die before foo.
  TestSessionPair p;
  p.server()->registerService("foo", gfoo);
  qi::AnyObject client = p.client()->service("foo");
  ASSERT_EQ(0, client.call<int>("count1"));
  qi::ProxySignal<void(int, int)> proxy1(client, "sig1");
  foo->subscribe1();
  proxy1(1, 2);
  PERSIST_ASSERT(, foo->count1() == 3, 500);
  // small hack, reuse foo function to test callback on proxy signal
  Foo foo2;
  qi::SignalLink l =  proxy1.connect(boost::bind(&Foo::on1, &foo2, _1, _2));
  proxy1(3, 4);
  PERSIST_ASSERT(, foo->count1() == 10, 500);
  PERSIST_ASSERT(, foo2.count1() == 7, 500);
  proxy1.disconnect(l);
  proxy1(1, 1);
  PERSIST_ASSERT(, foo->count1() == 12, 500);
  PERSIST_ASSERT(, foo2.count1() == 7, 500);
}

class Bar
{
public:
  Bar() :_count(0){}
  void set(int v) {prop.set(v);}
  int get() { return prop.get();}
  int count() { return _count;}
  void onProp(int v)
  {
    qiLogDebug() << "onprop " << v <<" " << this;
    _count += v;
  }
  void subscribe()
  {
    _link = prop.connect(boost::bind(&Bar::onProp, this, _1));
  }
  void unsubscribe()
  {
    prop.disconnect(_link);
  }
  qi::Property<int> prop;
  qi::SignalLink _link;
  int _count;
};

QI_REGISTER_OBJECT(Bar, subscribe, unsubscribe, count, get, set, prop);

TEST(Proxy, Property)
{
  boost::shared_ptr<Bar> bar(new Bar);
  qi::AnyObject gbar = qi::AnyReference::from(bar).toObject();
  ASSERT_TRUE(!!gbar);
  // The session must die before bar.
  TestSessionPair p;
  p.server()->registerService("bar", gbar);
  // we need that to force two clients
  p.server()->registerService("bar2", gbar);
  qi::AnyObject client = p.client()->service("bar");
  ASSERT_EQ(0, client.call<int>("count"));

  qi::ProxyProperty<int> pp(client, "prop");
  bar->set(1);
  ASSERT_EQ(1, pp.get());
  pp.set(2);
  ASSERT_EQ(2, bar->get());
  // althoug PropertyProxy::set is itself synchronous, notify on remote end
  // may be asynchronous, so subscribe below may come too soon and catch
  // the pp.set above
  qi::os::msleep(100);
  qiLogDebug() << "subscribe";
  bar->subscribe();
  qi::os::msleep(100);
  qiLogDebug() << "set 3";
  pp.set(3);
  // this is an event, all notify are asychronous
  PERSIST_ASSERT(, bar->count() == 3, 500);

  Bar bar2;
  qi::SignalLink l = pp.connect(boost::bind(&Bar::onProp, &bar2, _1));
  bar->set(4);
  // this one is async (remote notify of local property set)
  PERSIST_ASSERT(, bar2.count() == 4, 500);
  pp.disconnect(l);
  bar->set(5); // we expect an async op *not* to happen, no choice but wait.
  qi::os::msleep(200);
  ASSERT_EQ(4, bar2.count());
  // reconnect to see if disconnect did not break anything
  l = pp.connect(boost::bind(&Bar::onProp, &bar2, _1));
  bar->set(4);
  PERSIST_ASSERT(, bar2.count() == 8, 500);

  // proxy-proxy
  qi::AnyObject client2 = p.client()->service("bar2");
  qi::ProxyProperty<int> pp2(client2, "prop");
  Bar bar3;
  pp2.connect(boost::bind(&Bar::onProp, &bar3, _1));
  qiLogDebug() << "set 2";
  pp.set(2);
  PERSIST(, bar3.count() == 2, 1000);
  ASSERT_EQ(2, bar3.count());
  PERSIST(, bar2.count() == 10, 500);
  ASSERT_EQ(10, bar2.count());
  qiLogDebug() << "set 3";
  pp2.set(3);
  PERSIST_ASSERT(, bar2.count() == 13, 500);
  PERSIST_ASSERT(, bar3.count() == 5, 500);
}

int main(int argc, char **argv) {
  qi::Application app(argc, argv);
  TestMode::initTestMode(argc, argv);
  ::testing::InitGoogleTest(&argc, argv);
  return RUN_ALL_TESTS();
}
<commit_msg>test: test_proxy: rename count to sum<commit_after>/*
**  Copyright (C) 2013 Aldebaran Robotics
**  See COPYING for the license
*/

#include <list>
#include <iostream>

#include <gtest/gtest.h>

#include <qi/qi.hpp>
#include <qi/application.hpp>
#include <qi/eventloop.hpp>
#include <qitype/anyobject.hpp>
#include <qitype/objecttypebuilder.hpp>
#include <qitype/proxysignal.hpp>
#include <qitype/proxyproperty.hpp>
#include <qimessaging/session.hpp>
#include <testsession/testsessionpair.hpp>

#define PERSIST_CHECK(code, cond, what, msdelay)  \
do                                           \
{                                            \
  code;                                      \
  for(unsigned i=0; i<50 && !(cond); ++i)    \
  {                                          \
    qi::os::msleep(1 + msdelay / 50);        \
    code;                                    \
  }                                          \
  what(cond);                                \
} while(0)

#define PERSIST_ASSERT(code, cond, msdelay)  \
 PERSIST_CHECK(code, cond, ASSERT_TRUE, msdelay)

#define PERSIST_EXPECT(code, cond, msdelay)  \
 PERSIST_CHECK(code, cond, EXPECT_TRUE, msdelay)

//cast to void to avoid unused warning.
#define PERSIST(code, cond, msdelay)  \
  PERSIST_CHECK(code, cond, (void),msdelay)

qiLogCategory("test");

class Foo
{
public:
  Foo() : _count1(0), _count2(0) {}
  void subscribe1();
  void subscribe2();
  void unsubscribe1();
  void unsubscribe2();
  int count1() { return _count1;}
  int count2() { return _count2;}
  void on1(int a, int b) { _count1 += a+b;}
  void on2() { ++_count2;}
  qi::Signal<int, int> sig1;
  qi::Signal<> sig2;

  int _count1, _count2;
  qi::SignalLink _l1, _l2;
};

void Foo::subscribe1()
{
  _l1 = sig1.connect(boost::bind(&Foo::on1, this, _1, _2));
}
void Foo::unsubscribe1()
{
  sig1.disconnect(_l1);
}
void Foo::subscribe2()
{
  _l2 = sig2.connect(boost::bind(&Foo::on2, this));
}
void Foo::unsubscribe2()
{
  sig2.disconnect(_l2);
}

QI_REGISTER_OBJECT(Foo, subscribe1, subscribe2, unsubscribe1, unsubscribe2,
  count1, count2, sig1, sig2);

TEST(Proxy, Signal)
{
  boost::shared_ptr<Foo> foo(new Foo);
  qi::AnyObject gfoo = qi::AnyReference::from(foo).toObject();
  ASSERT_TRUE(!!gfoo);
  qi::details::printMetaObject(std::cerr, gfoo.metaObject());
  // The session must die before foo.
  TestSessionPair p;
  p.server()->registerService("foo", gfoo);
  qi::AnyObject client = p.client()->service("foo");
  ASSERT_EQ(0, client.call<int>("count1"));
  qi::ProxySignal<void(int, int)> proxy1(client, "sig1");
  foo->subscribe1();
  proxy1(1, 2);
  PERSIST_ASSERT(, foo->count1() == 3, 500);
  // small hack, reuse foo function to test callback on proxy signal
  Foo foo2;
  qi::SignalLink l =  proxy1.connect(boost::bind(&Foo::on1, &foo2, _1, _2));
  proxy1(3, 4);
  PERSIST_ASSERT(, foo->count1() == 10, 500);
  PERSIST_ASSERT(, foo2.count1() == 7, 500);
  proxy1.disconnect(l);
  proxy1(1, 1);
  PERSIST_ASSERT(, foo->count1() == 12, 500);
  PERSIST_ASSERT(, foo2.count1() == 7, 500);
}

class Bar
{
public:
  Bar() :_sum(0){}
  void set(int v) {prop.set(v);}
  int get() { return prop.get();}
  int sum() { return _sum;}
  void onProp(int v)
  {
    qiLogDebug() << "onprop " << v <<" " << this;
    _sum += v;
  }
  void subscribe()
  {
    _link = prop.connect(boost::bind(&Bar::onProp, this, _1));
  }
  void unsubscribe()
  {
    prop.disconnect(_link);
  }
  qi::Property<int> prop;
  qi::SignalLink _link;
  int _sum;
};

QI_REGISTER_OBJECT(Bar, subscribe, unsubscribe, sum, get, set, prop);

TEST(Proxy, Property)
{
  boost::shared_ptr<Bar> bar(new Bar);
  qi::AnyObject gbar = qi::AnyReference::from(bar).toObject();
  ASSERT_TRUE(!!gbar);
  // The session must die before bar.
  TestSessionPair p;
  p.server()->registerService("bar", gbar);
  // we need that to force two clients
  p.server()->registerService("bar2", gbar);
  qi::AnyObject client = p.client()->service("bar");
  ASSERT_EQ(0, client.call<int>("sum"));

  qi::ProxyProperty<int> pp(client, "prop");
  bar->set(1);
  ASSERT_EQ(1, pp.get());
  pp.set(2);
  ASSERT_EQ(2, bar->get());
  // althoug PropertyProxy::set is itself synchronous, notify on remote end
  // may be asynchronous, so subscribe below may come too soon and catch
  // the pp.set above
  qi::os::msleep(100);
  qiLogDebug() << "subscribe";
  bar->subscribe();
  qi::os::msleep(100);
  qiLogDebug() << "set 3";
  pp.set(3);
  // this is an event, all notify are asychronous
  PERSIST_ASSERT(, bar->sum() == 3, 500);

  Bar bar2;
  qi::SignalLink l = pp.connect(boost::bind(&Bar::onProp, &bar2, _1));
  bar->set(4);
  // this one is async (remote notify of local property set)
  PERSIST_ASSERT(, bar2.sum() == 4, 500);
  pp.disconnect(l);
  bar->set(5); // we expect an async op *not* to happen, no choice but wait.
  qi::os::msleep(200);
  ASSERT_EQ(4, bar2.sum());
  // reconnect to see if disconnect did not break anything
  l = pp.connect(boost::bind(&Bar::onProp, &bar2, _1));
  bar->set(4);
  PERSIST_ASSERT(, bar2.sum() == 8, 500);

  // proxy-proxy
  qi::AnyObject client2 = p.client()->service("bar2");
  qi::ProxyProperty<int> pp2(client2, "prop");
  Bar bar3;
  pp2.connect(boost::bind(&Bar::onProp, &bar3, _1));
  qiLogDebug() << "set 2";
  pp.set(2);
  PERSIST(, bar3.sum() == 2, 1000);
  ASSERT_EQ(2, bar3.sum());
  PERSIST(, bar2.sum() == 10, 500);
  ASSERT_EQ(10, bar2.sum());
  qiLogDebug() << "set 3";
  pp2.set(3);
  PERSIST_ASSERT(, bar2.sum() == 13, 500);
  PERSIST_ASSERT(, bar3.sum() == 5, 500);
}

int main(int argc, char **argv) {
  qi::Application app(argc, argv);
  TestMode::initTestMode(argc, argv);
  ::testing::InitGoogleTest(&argc, argv);
  return RUN_ALL_TESTS();
}
<|endoftext|>


<commit_before>/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements. See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership. The ASF licenses this file
 * to you 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.
 */

#include <cassert>
#include <algorithm>

#include <transport/TBufferTransports.h>

using std::string;

namespace apache { namespace thrift { namespace transport {

uint32_t TBufferedTransport::readSlow(uint8_t* buf, uint32_t len) {
  uint32_t have = rBound_ - rBase_;

  // We should only take the slow path if we can't satisfy the read
  // with the data already in the buffer.
  assert(have < len);

  // If we have some date in the buffer, copy it out and return it.
  // We have to return it without attempting to read more, since we aren't
  // guaranteed that the underlying transport actually has more data, so
  // attempting to read from it could block.
  if (have > 0) {
    memcpy(buf, rBase_, have);
    setReadBuffer(rBuf_.get(), 0);
    return have;
  }

  // No data is available in our buffer.
  // Get more from underlying transport up to buffer size.
  // Note that this makes a lot of sense if len < rBufSize_
  // and almost no sense otherwise.  TODO(dreiss): Fix that
  // case (possibly including some readv hotness).
  setReadBuffer(rBuf_.get(), transport_->read(rBuf_.get(), rBufSize_));

  // Hand over whatever we have.
  uint32_t give = std::min(len, static_cast<uint32_t>(rBound_ - rBase_));
  memcpy(buf, rBase_, give);
  rBase_ += give;

  return give;
}

void TBufferedTransport::writeSlow(const uint8_t* buf, uint32_t len) {
  uint32_t have_bytes = wBase_ - wBuf_.get();
  uint32_t space = wBound_ - wBase_;
  // We should only take the slow path if we can't accomodate the write
  // with the free space already in the buffer.
  assert(wBound_ - wBase_ < static_cast<ptrdiff_t>(len));

  // Now here's the tricky question: should we copy data from buf into our
  // internal buffer and write it from there, or should we just write out
  // the current internal buffer in one syscall and write out buf in another.
  // If our currently buffered data plus buf is at least double our buffer
  // size, we will have to do two syscalls no matter what (except in the
  // degenerate case when our buffer is empty), so there is no use copying.
  // Otherwise, there is sort of a sliding scale.  If we have N-1 bytes
  // buffered and need to write 2, it would be crazy to do two syscalls.
  // On the other hand, if we have 2 bytes buffered and are writing 2N-3,
  // we can save a syscall in the short term by loading up our buffer, writing
  // it out, and copying the rest of the bytes into our buffer.  Of course,
  // if we get another 2-byte write, we haven't saved any syscalls at all,
  // and have just copied nearly 2N bytes for nothing.  Finding a perfect
  // policy would require predicting the size of future writes, so we're just
  // going to always eschew syscalls if we have less than 2N bytes to write.

  // The case where we have to do two syscalls.
  // This case also covers the case where the buffer is empty,
  // but it is clearer (I think) to think of it as two separate cases.
  if ((have_bytes + len >= 2*wBufSize_) || (have_bytes == 0)) {
    // TODO(dreiss): writev
    if (have_bytes > 0) {
      transport_->write(wBuf_.get(), have_bytes);
    }
    transport_->write(buf, len);
    wBase_ = wBuf_.get();
    return;
  }

  // Fill up our internal buffer for a write.
  memcpy(wBase_, buf, space);
  buf += space;
  len -= space;
  transport_->write(wBuf_.get(), wBufSize_);

  // Copy the rest into our buffer.
  assert(len < wBufSize_);
  memcpy(wBuf_.get(), buf, len);
  wBase_ = wBuf_.get() + len;
  return;
}

const uint8_t* TBufferedTransport::borrowSlow(uint8_t* buf, uint32_t* len) {
  (void) buf;
  (void) len;
  // Simply return NULL.  We don't know if there is actually data available on
  // the underlying transport, so calling read() might block.
  return NULL;
}

void TBufferedTransport::flush()  {
  // Write out any data waiting in the write buffer.
  uint32_t have_bytes = wBase_ - wBuf_.get();
  if (have_bytes > 0) {
    // Note that we reset wBase_ prior to the underlying write
    // to ensure we're in a sane state (i.e. internal buffer cleaned)
    // if the underlying write throws up an exception
    wBase_ = wBuf_.get();
    transport_->write(wBuf_.get(), have_bytes);
  }

  // Flush the underlying transport.
  transport_->flush();
}

uint32_t TFramedTransport::readSlow(uint8_t* buf, uint32_t len) {
  uint32_t want = len;
  uint32_t have = rBound_ - rBase_;

  // We should only take the slow path if we can't satisfy the read
  // with the data already in the buffer.
  assert(have < want);

  // Copy out whatever we have.
  if (have > 0) {
    memcpy(buf, rBase_, have);
    want -= have;
    buf += have;
  }

  // Read another frame.
  if (!readFrame()) {
    // EOF.  No frame available.
    return 0;
  }

  // TODO(dreiss): Should we warn when reads cross frames?

  // Hand over whatever we have.
  uint32_t give = std::min(want, static_cast<uint32_t>(rBound_ - rBase_));
  memcpy(buf, rBase_, give);
  rBase_ += give;
  want -= give;

  return (len - want);
}

bool TFramedTransport::readFrame() {
  // TODO(dreiss): Think about using readv here, even though it would
  // result in (gasp) read-ahead.

  // Read the size of the next frame.
  // We can't use readAll(&sz, sizeof(sz)), since that always throws an
  // exception on EOF.  We want to throw an exception only if EOF occurs after
  // partial size data.
  int32_t sz;
  uint32_t size_bytes_read = 0;
  while (size_bytes_read < sizeof(sz)) {
    uint8_t* szp = reinterpret_cast<uint8_t*>(&sz) + size_bytes_read;
    uint32_t bytes_read = transport_->read(szp, sizeof(sz) - size_bytes_read);
    if (bytes_read == 0) {
      if (size_bytes_read == 0) {
        // EOF before any data was read.
        return false;
      } else {
        // EOF after a partial frame header.  Raise an exception.
        throw TTransportException(TTransportException::END_OF_FILE,
                                  "No more data to read after "
                                  "partial frame header.");
      }
    }
    size_bytes_read += bytes_read;
  }

  sz = ntohl(sz);

  if (sz < 0) {
    throw TTransportException("Frame size has negative value");
  }

  // Read the frame payload, and reset markers.
  if (sz > static_cast<int32_t>(rBufSize_)) {
    rBuf_.reset(new uint8_t[sz]);
    rBufSize_ = sz;
  }
  transport_->readAll(rBuf_.get(), sz);
  setReadBuffer(rBuf_.get(), sz);
  return true;
}

void TFramedTransport::writeSlow(const uint8_t* buf, uint32_t len) {
  // Double buffer size until sufficient.
  uint32_t have = wBase_ - wBuf_.get();
  uint32_t new_size = wBufSize_;
  while (new_size < len + have) {
    new_size = new_size > 0 ? new_size * 2 : 1;
  }

  // TODO(dreiss): Consider modifying this class to use malloc/free
  // so we can use realloc here.

  // Allocate new buffer.
  uint8_t* new_buf = new uint8_t[new_size];

  // Copy the old buffer to the new one.
  memcpy(new_buf, wBuf_.get(), have);

  // Now point buf to the new one.
  wBuf_.reset(new_buf);
  wBufSize_ = new_size;
  wBase_ = wBuf_.get() + have;
  wBound_ = wBuf_.get() + wBufSize_;

  // Copy the data into the new buffer.
  memcpy(wBase_, buf, len);
  wBase_ += len;
}

void TFramedTransport::flush()  {
  int32_t sz_hbo, sz_nbo;
  assert(wBufSize_ > sizeof(sz_nbo));

  // Slip the frame size into the start of the buffer.
  sz_hbo = wBase_ - (wBuf_.get() + sizeof(sz_nbo));
  sz_nbo = (int32_t)htonl((uint32_t)(sz_hbo));
  memcpy(wBuf_.get(), (uint8_t*)&sz_nbo, sizeof(sz_nbo));

  if (sz_hbo > 0) {
    // Note that we reset wBase_ (with a pad for the frame size)
    // prior to the underlying write to ensure we're in a sane state
    // (i.e. internal buffer cleaned) if the underlying write throws
    // up an exception
    wBase_ = wBuf_.get() + sizeof(sz_nbo);

    // Write size and frame body.
    transport_->write(wBuf_.get(), sizeof(sz_nbo)+sz_hbo);
  }

  // Flush the underlying transport.
  transport_->flush();
}

uint32_t TFramedTransport::writeEnd() {
  return wBase_ - wBuf_.get();
}

const uint8_t* TFramedTransport::borrowSlow(uint8_t* buf, uint32_t* len) {
  (void) buf;
  (void) len;
  // Don't try to be clever with shifting buffers.
  // If the fast path failed let the protocol use its slow path.
  // Besides, who is going to try to borrow across messages?
  return NULL;
}

uint32_t TFramedTransport::readEnd() {
  // include framing bytes
  return rBound_ - rBuf_.get() + sizeof(uint32_t);
}

void TMemoryBuffer::computeRead(uint32_t len, uint8_t** out_start, uint32_t* out_give) {
  // Correct rBound_ so we can use the fast path in the future.
  rBound_ = wBase_;

  // Decide how much to give.
  uint32_t give = std::min(len, available_read());

  *out_start = rBase_;
  *out_give = give;

  // Preincrement rBase_ so the caller doesn't have to.
  rBase_ += give;
}

uint32_t TMemoryBuffer::readSlow(uint8_t* buf, uint32_t len) {
  uint8_t* start;
  uint32_t give;
  computeRead(len, &start, &give);

  // Copy into the provided buffer.
  memcpy(buf, start, give);

  return give;
}

uint32_t TMemoryBuffer::readAppendToString(std::string& str, uint32_t len) {
  // Don't get some stupid assertion failure.
  if (buffer_ == NULL) {
    return 0;
  }

  uint8_t* start;
  uint32_t give;
  computeRead(len, &start, &give);

  // Append to the provided string.
  str.append((char*)start, give);

  return give;
}

void TMemoryBuffer::ensureCanWrite(uint32_t len) {
  // Check available space
  uint32_t avail = available_write();
  if (len <= avail) {
    return;
  }

  if (!owner_) {
    throw TTransportException("Insufficient space in external MemoryBuffer");
  }

  // Grow the buffer as necessary.
  uint32_t new_size = bufferSize_;
  while (len > avail) {
    new_size = new_size > 0 ? new_size * 2 : 1;
    avail = available_write() + (new_size - bufferSize_);
  }

  // Allocate into a new pointer so we don't bork ours if it fails.
  void* new_buffer = std::realloc(buffer_, new_size);
  if (new_buffer == NULL) {
    throw std::bad_alloc();
  }
  bufferSize_ = new_size;

  ptrdiff_t offset = (uint8_t*)new_buffer - buffer_;
  buffer_ += offset;
  rBase_ += offset;
  rBound_ += offset;
  wBase_ += offset;
  wBound_ = buffer_ + bufferSize_;
}

void TMemoryBuffer::writeSlow(const uint8_t* buf, uint32_t len) {
  ensureCanWrite(len);

  // Copy into the buffer and increment wBase_.
  memcpy(wBase_, buf, len);
  wBase_ += len;
}

void TMemoryBuffer::wroteBytes(uint32_t len) {
  uint32_t avail = available_write();
  if (len > avail) {
    throw TTransportException("Client wrote more bytes than size of buffer.");
  }
  wBase_ += len;
}

const uint8_t* TMemoryBuffer::borrowSlow(uint8_t* buf, uint32_t* len) {
  (void) buf;
  rBound_ = wBase_;
  if (available_read() >= *len) {
    *len = available_read();
    return rBase_;
  }
  return NULL;
}

}}} // apache::thrift::transport
<commit_msg>Thrift-1451:FramedTransport: Prevent infinite loop when writing Client: cpp Patch: Dave Watson<commit_after>/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements. See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership. The ASF licenses this file
 * to you 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.
 */

#include <cassert>
#include <algorithm>

#include <transport/TBufferTransports.h>

using std::string;

namespace apache { namespace thrift { namespace transport {

uint32_t TBufferedTransport::readSlow(uint8_t* buf, uint32_t len) {
  uint32_t have = rBound_ - rBase_;

  // We should only take the slow path if we can't satisfy the read
  // with the data already in the buffer.
  assert(have < len);

  // If we have some date in the buffer, copy it out and return it.
  // We have to return it without attempting to read more, since we aren't
  // guaranteed that the underlying transport actually has more data, so
  // attempting to read from it could block.
  if (have > 0) {
    memcpy(buf, rBase_, have);
    setReadBuffer(rBuf_.get(), 0);
    return have;
  }

  // No data is available in our buffer.
  // Get more from underlying transport up to buffer size.
  // Note that this makes a lot of sense if len < rBufSize_
  // and almost no sense otherwise.  TODO(dreiss): Fix that
  // case (possibly including some readv hotness).
  setReadBuffer(rBuf_.get(), transport_->read(rBuf_.get(), rBufSize_));

  // Hand over whatever we have.
  uint32_t give = std::min(len, static_cast<uint32_t>(rBound_ - rBase_));
  memcpy(buf, rBase_, give);
  rBase_ += give;

  return give;
}

void TBufferedTransport::writeSlow(const uint8_t* buf, uint32_t len) {
  uint32_t have_bytes = wBase_ - wBuf_.get();
  uint32_t space = wBound_ - wBase_;
  // We should only take the slow path if we can't accomodate the write
  // with the free space already in the buffer.
  assert(wBound_ - wBase_ < static_cast<ptrdiff_t>(len));

  // Now here's the tricky question: should we copy data from buf into our
  // internal buffer and write it from there, or should we just write out
  // the current internal buffer in one syscall and write out buf in another.
  // If our currently buffered data plus buf is at least double our buffer
  // size, we will have to do two syscalls no matter what (except in the
  // degenerate case when our buffer is empty), so there is no use copying.
  // Otherwise, there is sort of a sliding scale.  If we have N-1 bytes
  // buffered and need to write 2, it would be crazy to do two syscalls.
  // On the other hand, if we have 2 bytes buffered and are writing 2N-3,
  // we can save a syscall in the short term by loading up our buffer, writing
  // it out, and copying the rest of the bytes into our buffer.  Of course,
  // if we get another 2-byte write, we haven't saved any syscalls at all,
  // and have just copied nearly 2N bytes for nothing.  Finding a perfect
  // policy would require predicting the size of future writes, so we're just
  // going to always eschew syscalls if we have less than 2N bytes to write.

  // The case where we have to do two syscalls.
  // This case also covers the case where the buffer is empty,
  // but it is clearer (I think) to think of it as two separate cases.
  if ((have_bytes + len >= 2*wBufSize_) || (have_bytes == 0)) {
    // TODO(dreiss): writev
    if (have_bytes > 0) {
      transport_->write(wBuf_.get(), have_bytes);
    }
    transport_->write(buf, len);
    wBase_ = wBuf_.get();
    return;
  }

  // Fill up our internal buffer for a write.
  memcpy(wBase_, buf, space);
  buf += space;
  len -= space;
  transport_->write(wBuf_.get(), wBufSize_);

  // Copy the rest into our buffer.
  assert(len < wBufSize_);
  memcpy(wBuf_.get(), buf, len);
  wBase_ = wBuf_.get() + len;
  return;
}

const uint8_t* TBufferedTransport::borrowSlow(uint8_t* buf, uint32_t* len) {
  (void) buf;
  (void) len;
  // Simply return NULL.  We don't know if there is actually data available on
  // the underlying transport, so calling read() might block.
  return NULL;
}

void TBufferedTransport::flush()  {
  // Write out any data waiting in the write buffer.
  uint32_t have_bytes = wBase_ - wBuf_.get();
  if (have_bytes > 0) {
    // Note that we reset wBase_ prior to the underlying write
    // to ensure we're in a sane state (i.e. internal buffer cleaned)
    // if the underlying write throws up an exception
    wBase_ = wBuf_.get();
    transport_->write(wBuf_.get(), have_bytes);
  }

  // Flush the underlying transport.
  transport_->flush();
}

uint32_t TFramedTransport::readSlow(uint8_t* buf, uint32_t len) {
  uint32_t want = len;
  uint32_t have = rBound_ - rBase_;

  // We should only take the slow path if we can't satisfy the read
  // with the data already in the buffer.
  assert(have < want);

  // Copy out whatever we have.
  if (have > 0) {
    memcpy(buf, rBase_, have);
    want -= have;
    buf += have;
  }

  // Read another frame.
  if (!readFrame()) {
    // EOF.  No frame available.
    return 0;
  }

  // TODO(dreiss): Should we warn when reads cross frames?

  // Hand over whatever we have.
  uint32_t give = std::min(want, static_cast<uint32_t>(rBound_ - rBase_));
  memcpy(buf, rBase_, give);
  rBase_ += give;
  want -= give;

  return (len - want);
}

bool TFramedTransport::readFrame() {
  // TODO(dreiss): Think about using readv here, even though it would
  // result in (gasp) read-ahead.

  // Read the size of the next frame.
  // We can't use readAll(&sz, sizeof(sz)), since that always throws an
  // exception on EOF.  We want to throw an exception only if EOF occurs after
  // partial size data.
  int32_t sz;
  uint32_t size_bytes_read = 0;
  while (size_bytes_read < sizeof(sz)) {
    uint8_t* szp = reinterpret_cast<uint8_t*>(&sz) + size_bytes_read;
    uint32_t bytes_read = transport_->read(szp, sizeof(sz) - size_bytes_read);
    if (bytes_read == 0) {
      if (size_bytes_read == 0) {
        // EOF before any data was read.
        return false;
      } else {
        // EOF after a partial frame header.  Raise an exception.
        throw TTransportException(TTransportException::END_OF_FILE,
                                  "No more data to read after "
                                  "partial frame header.");
      }
    }
    size_bytes_read += bytes_read;
  }

  sz = ntohl(sz);

  if (sz < 0) {
    throw TTransportException("Frame size has negative value");
  }

  // Read the frame payload, and reset markers.
  if (sz > static_cast<int32_t>(rBufSize_)) {
    rBuf_.reset(new uint8_t[sz]);
    rBufSize_ = sz;
  }
  transport_->readAll(rBuf_.get(), sz);
  setReadBuffer(rBuf_.get(), sz);
  return true;
}

void TFramedTransport::writeSlow(const uint8_t* buf, uint32_t len) {
  // Double buffer size until sufficient.
  uint32_t have = wBase_ - wBuf_.get();
  uint32_t new_size = wBufSize_;
  if (len + have < have /* overflow */ || len + have > 0x7fffffff) {
    throw TTransportException(TTransportException::BAD_ARGS,
        "Attempted to write over 2 GB to TFramedTransport.");
  }
  while (new_size < len + have) {
    new_size = new_size > 0 ? new_size * 2 : 1;
  }

  // TODO(dreiss): Consider modifying this class to use malloc/free
  // so we can use realloc here.

  // Allocate new buffer.
  uint8_t* new_buf = new uint8_t[new_size];

  // Copy the old buffer to the new one.
  memcpy(new_buf, wBuf_.get(), have);

  // Now point buf to the new one.
  wBuf_.reset(new_buf);
  wBufSize_ = new_size;
  wBase_ = wBuf_.get() + have;
  wBound_ = wBuf_.get() + wBufSize_;

  // Copy the data into the new buffer.
  memcpy(wBase_, buf, len);
  wBase_ += len;
}

void TFramedTransport::flush()  {
  int32_t sz_hbo, sz_nbo;
  assert(wBufSize_ > sizeof(sz_nbo));

  // Slip the frame size into the start of the buffer.
  sz_hbo = wBase_ - (wBuf_.get() + sizeof(sz_nbo));
  sz_nbo = (int32_t)htonl((uint32_t)(sz_hbo));
  memcpy(wBuf_.get(), (uint8_t*)&sz_nbo, sizeof(sz_nbo));

  if (sz_hbo > 0) {
    // Note that we reset wBase_ (with a pad for the frame size)
    // prior to the underlying write to ensure we're in a sane state
    // (i.e. internal buffer cleaned) if the underlying write throws
    // up an exception
    wBase_ = wBuf_.get() + sizeof(sz_nbo);

    // Write size and frame body.
    transport_->write(wBuf_.get(), sizeof(sz_nbo)+sz_hbo);
  }

  // Flush the underlying transport.
  transport_->flush();
}

uint32_t TFramedTransport::writeEnd() {
  return wBase_ - wBuf_.get();
}

const uint8_t* TFramedTransport::borrowSlow(uint8_t* buf, uint32_t* len) {
  (void) buf;
  (void) len;
  // Don't try to be clever with shifting buffers.
  // If the fast path failed let the protocol use its slow path.
  // Besides, who is going to try to borrow across messages?
  return NULL;
}

uint32_t TFramedTransport::readEnd() {
  // include framing bytes
  return rBound_ - rBuf_.get() + sizeof(uint32_t);
}

void TMemoryBuffer::computeRead(uint32_t len, uint8_t** out_start, uint32_t* out_give) {
  // Correct rBound_ so we can use the fast path in the future.
  rBound_ = wBase_;

  // Decide how much to give.
  uint32_t give = std::min(len, available_read());

  *out_start = rBase_;
  *out_give = give;

  // Preincrement rBase_ so the caller doesn't have to.
  rBase_ += give;
}

uint32_t TMemoryBuffer::readSlow(uint8_t* buf, uint32_t len) {
  uint8_t* start;
  uint32_t give;
  computeRead(len, &start, &give);

  // Copy into the provided buffer.
  memcpy(buf, start, give);

  return give;
}

uint32_t TMemoryBuffer::readAppendToString(std::string& str, uint32_t len) {
  // Don't get some stupid assertion failure.
  if (buffer_ == NULL) {
    return 0;
  }

  uint8_t* start;
  uint32_t give;
  computeRead(len, &start, &give);

  // Append to the provided string.
  str.append((char*)start, give);

  return give;
}

void TMemoryBuffer::ensureCanWrite(uint32_t len) {
  // Check available space
  uint32_t avail = available_write();
  if (len <= avail) {
    return;
  }

  if (!owner_) {
    throw TTransportException("Insufficient space in external MemoryBuffer");
  }

  // Grow the buffer as necessary.
  uint32_t new_size = bufferSize_;
  while (len > avail) {
    new_size = new_size > 0 ? new_size * 2 : 1;
    avail = available_write() + (new_size - bufferSize_);
  }

  // Allocate into a new pointer so we don't bork ours if it fails.
  void* new_buffer = std::realloc(buffer_, new_size);
  if (new_buffer == NULL) {
    throw std::bad_alloc();
  }
  bufferSize_ = new_size;

  ptrdiff_t offset = (uint8_t*)new_buffer - buffer_;
  buffer_ += offset;
  rBase_ += offset;
  rBound_ += offset;
  wBase_ += offset;
  wBound_ = buffer_ + bufferSize_;
}

void TMemoryBuffer::writeSlow(const uint8_t* buf, uint32_t len) {
  ensureCanWrite(len);

  // Copy into the buffer and increment wBase_.
  memcpy(wBase_, buf, len);
  wBase_ += len;
}

void TMemoryBuffer::wroteBytes(uint32_t len) {
  uint32_t avail = available_write();
  if (len > avail) {
    throw TTransportException("Client wrote more bytes than size of buffer.");
  }
  wBase_ += len;
}

const uint8_t* TMemoryBuffer::borrowSlow(uint8_t* buf, uint32_t* len) {
  (void) buf;
  rBound_ = wBase_;
  if (available_read() >= *len) {
    *len = available_read();
    return rBase_;
  }
  return NULL;
}

}}} // apache::thrift::transport
<|endoftext|>


<commit_before>/*
 * SDL2 skeleton for lab assignments 1–3 of the KTH course DH2323,
 * Computer Graphics and Interaction (and also SU DA3001, Datalogi II).
 *
 * See README.md for details.
 */

#include <iostream>
#include <glm/glm.hpp>
#include <vector>
#include "SDL2auxiliary.h"

using namespace std;
using glm::vec3;

// --------------------------------------------------------
// GLOBAL VARIABLES

const int SCREEN_WIDTH = 640;
const int SCREEN_HEIGHT = 480;
SDL2Aux *sdlAux;

// --------------------------------------------------------
// FUNCTION DECLARATIONS

void Draw();

// --------------------------------------------------------
// FUNCTION DEFINITIONS

int main(int argc, char* argv[]) {
  sdlAux = new SDL2Aux(SCREEN_WIDTH, SCREEN_HEIGHT);

  while (!sdlAux->quitEvent()) {
    Draw();
  }

  sdlAux->saveBMP("screenshot.bmp");
  return 0;
}

void Draw() {
  for (int y = 0; y < SCREEN_HEIGHT; ++y) {
    for (int x = 0; x < SCREEN_WIDTH; ++x) {
      vec3 color(0.0, 0.0, 1.0);
      sdlAux->putPixel(x, y, color);
    }
  }

  sdlAux->render();
}
<commit_msg>Added clear pixels to skeleton<commit_after>/*
 * SDL2 skeleton for lab assignments 1–3 of the KTH course DH2323,
 * Computer Graphics and Interaction (and also SU DA3001, Datalogi II).
 *
 * See README.md for details.
 */

#include <iostream>
#include <glm/glm.hpp>
#include <vector>
#include "SDL2auxiliary.h"

using namespace std;
using glm::vec3;

// --------------------------------------------------------
// GLOBAL VARIABLES

const int SCREEN_WIDTH = 640;
const int SCREEN_HEIGHT = 480;
SDL2Aux *sdlAux;

// --------------------------------------------------------
// FUNCTION DECLARATIONS

void Draw();

// --------------------------------------------------------
// FUNCTION DEFINITIONS

int main(int argc, char* argv[]) {
  sdlAux = new SDL2Aux(SCREEN_WIDTH, SCREEN_HEIGHT);

  while (!sdlAux->quitEvent()) {
    Draw();
  }

  sdlAux->saveBMP("screenshot.bmp");
  return 0;
}

void Draw() {
  sdlAux->clearPixels();

  for (int y = 0; y < SCREEN_HEIGHT; ++y) {
    for (int x = 0; x < SCREEN_WIDTH; ++x) {
      vec3 color(0.0, 0.0, 1.0);
      sdlAux->putPixel(x, y, color);
    }
  }

  sdlAux->render();
}
<|endoftext|>