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

#include <quanta/core/config.hpp>
#include <togo/core/error/assert.hpp>
#include <togo/core/utility/utility.hpp>
#include <togo/core/memory/memory.hpp>

#include <quanta/core/string/unmanaged_string.hpp>
#include <quanta/core/object/object.hpp>

namespace quanta {

/// Set type.
///
/// Returns true if type changed.
bool object::set_type(Object& obj, ObjectValueType const type) {
	if (object::type(obj) == type) {
		return false;
	}
	object::clear_value(obj);
	obj.properties = (obj.properties & ~Object::M_TYPE) | unsigned_cast(type);
	return true;
}

/// Reset value to type default.
void object::clear_value(Object& obj) {
	auto& a = memory::default_allocator();
	switch (object::type(obj)) {
	case ObjectValueType::null:
		break;
	case ObjectValueType::boolean:
		obj.value.boolean = false;
		break;
	case ObjectValueType::integer:
		obj.value.numeric.integer = 0;
		unmanaged_string::clear(obj.value.numeric.unit, a);
		break;
	case ObjectValueType::decimal:
		obj.value.numeric.decimal = 0.0f;
		unmanaged_string::clear(obj.value.numeric.unit, a);
		break;
	case ObjectValueType::string:
		unmanaged_string::clear(obj.value.string, a);
		break;
	}
}

/// Copy an object.
void object::copy(Object& dst, Object const& src) {
	auto& a = memory::default_allocator();
	dst.source = src.source;
	dst.sub_source = src.sub_source;
	dst.properties = src.properties;
	unmanaged_string::set(dst.name, src.name, a);
	object::set_type(dst, object::type(src));
	switch (object::type(dst)) {
	case ObjectValueType::null:
		break;
	case ObjectValueType::boolean:
		dst.value.boolean = src.value.boolean;
		break;
	case ObjectValueType::integer:
		dst.value.numeric.integer = src.value.numeric.integer;
		unmanaged_string::set(dst.value.numeric.unit, src.value.numeric.unit, a);
		break;
	case ObjectValueType::decimal:
		dst.value.numeric.decimal = src.value.numeric.decimal;
		unmanaged_string::set(dst.value.numeric.unit, src.value.numeric.unit, a);
		break;
	case ObjectValueType::string:
		unmanaged_string::set(dst.value.string, src.value.string, a);
		break;
	}
	array::copy(dst.tags, src.tags);
	array::copy(dst.children, src.children);
	if (object::has_quantity(src)) {
		if (object::has_quantity(dst)) {
			object::copy(*dst.quantity, *src.quantity);
		} else {
			dst.quantity = TOGO_CONSTRUCT(a, Object, *src.quantity);
		}
	}
}

/// Create quantity or clear existing quantity.
Object& object::make_quantity(Object& obj) {
	if (object::has_quantity(obj)) {
		object::clear(*obj.quantity);
	} else {
		obj.quantity = TOGO_CONSTRUCT_DEFAULT(memory::default_allocator(), Object);
	}
	return *obj.quantity;
}

/// Clear all properties.
void object::clear(Object& obj) {
	object::set_null(obj);
	object::clear_name(obj);
	object::clear_source(obj);
	object::clear_tags(obj);
	object::clear_children(obj);
	object::clear_quantity(obj);
}

}

} // namespace quanta
<commit_msg>lib/core/object/object: added find_tag() and find_child().<commit_after>#line 2 "quanta/core/object/object.cpp"
/**
@copyright MIT license; see @ref index or the accompanying LICENSE file.
*/

#include <quanta/core/config.hpp>
#include <togo/core/log/log.hpp>
#include <togo/core/error/assert.hpp>
#include <togo/core/utility/utility.hpp>
#include <togo/core/memory/memory.hpp>

#include <quanta/core/string/unmanaged_string.hpp>
#include <quanta/core/object/object.hpp>

namespace quanta {

/// Set type.
///
/// Returns true if type changed.
bool object::set_type(Object& obj, ObjectValueType const type) {
	if (object::type(obj) == type) {
		return false;
	}
	object::clear_value(obj);
	obj.properties = (obj.properties & ~Object::M_TYPE) | unsigned_cast(type);
	return true;
}

/// Reset value to type default.
void object::clear_value(Object& obj) {
	auto& a = memory::default_allocator();
	switch (object::type(obj)) {
	case ObjectValueType::null:
		break;
	case ObjectValueType::boolean:
		obj.value.boolean = false;
		break;
	case ObjectValueType::integer:
		obj.value.numeric.integer = 0;
		unmanaged_string::clear(obj.value.numeric.unit, a);
		break;
	case ObjectValueType::decimal:
		obj.value.numeric.decimal = 0.0f;
		unmanaged_string::clear(obj.value.numeric.unit, a);
		break;
	case ObjectValueType::string:
		unmanaged_string::clear(obj.value.string, a);
		break;
	}
}

/// Copy an object.
void object::copy(Object& dst, Object const& src) {
	auto& a = memory::default_allocator();
	dst.source = src.source;
	dst.sub_source = src.sub_source;
	dst.properties = src.properties;
	unmanaged_string::set(dst.name, src.name, a);
	object::set_type(dst, object::type(src));
	switch (object::type(dst)) {
	case ObjectValueType::null:
		break;
	case ObjectValueType::boolean:
		dst.value.boolean = src.value.boolean;
		break;
	case ObjectValueType::integer:
		dst.value.numeric.integer = src.value.numeric.integer;
		unmanaged_string::set(dst.value.numeric.unit, src.value.numeric.unit, a);
		break;
	case ObjectValueType::decimal:
		dst.value.numeric.decimal = src.value.numeric.decimal;
		unmanaged_string::set(dst.value.numeric.unit, src.value.numeric.unit, a);
		break;
	case ObjectValueType::string:
		unmanaged_string::set(dst.value.string, src.value.string, a);
		break;
	}
	array::copy(dst.tags, src.tags);
	array::copy(dst.children, src.children);
	if (object::has_quantity(src)) {
		if (object::has_quantity(dst)) {
			object::copy(*dst.quantity, *src.quantity);
		} else {
			dst.quantity = TOGO_CONSTRUCT(a, Object, *src.quantity);
		}
	}
}

/// Create quantity or clear existing quantity.
Object& object::make_quantity(Object& obj) {
	if (object::has_quantity(obj)) {
		object::clear(*obj.quantity);
	} else {
		obj.quantity = TOGO_CONSTRUCT_DEFAULT(memory::default_allocator(), Object);
	}
	return *obj.quantity;
}

/// Clear all properties.
void object::clear(Object& obj) {
	object::set_null(obj);
	object::clear_name(obj);
	object::clear_source(obj);
	object::clear_tags(obj);
	object::clear_children(obj);
	object::clear_quantity(obj);
}

IGEN_PRIVATE
Object const* object::find_impl(
	Array<Object> const& collection,
	StringRef const& name,
	ObjectNameHash const name_hash
) {
	if (name_hash == OBJECT_NAME_NULL || array::empty(collection)) {
		return nullptr;
	}
	for (Object const& item : collection) {
		if (name_hash == item.name.hash) {
			#if defined(TOGO_DEBUG)
			if (name.valid() && !string::compare_equal(name, object::name(item))) {
				TOGO_LOG_DEBUGF(
					"hashes matched, but names mismatched: '%.*s' != '%.*s' (lookup_name != name)\n",
					name.size, name.data,
					item.name.size, item.name.data
				);
			}
			#else
				(void)name;
			#endif
			return &item;
		}
	}
	return nullptr;
}

/// Find tag by name.
Object* object::find_tag(Object& obj, StringRef const& name) {
	ObjectNameHash const name_hash = object::hash_name(name);
	return const_cast<Object*>(object::find_impl(obj.tags, name, name_hash));
}

/// Find tag by name.
Object const* object::find_tag(Object const& obj, StringRef const& name) {
	ObjectNameHash const name_hash = object::hash_name(name);
	return object::find_impl(obj.tags, name, name_hash);
}

/// Find tag by name hash.
Object* object::find_tag(Object& obj, ObjectNameHash const name_hash) {
	return const_cast<Object*>(
		object::find_impl(obj.tags, StringRef{}, name_hash)
	);
}

/// Find tag by name hash.
Object const* object::find_tag(Object const& obj, ObjectNameHash const name_hash) {
	return object::find_impl(obj.tags, StringRef{}, name_hash);
}

/// Find tag by name.
Object* object::find_child(Object& obj, StringRef const& name) {
	ObjectNameHash const name_hash = object::hash_name(name);
	return const_cast<Object*>(object::find_impl(obj.children, name, name_hash));
}

/// Find tag by name.
Object const* object::find_child(Object const& obj, StringRef const& name) {
	ObjectNameHash const name_hash = object::hash_name(name);
	return object::find_impl(obj.children, name, name_hash);
}

/// Find tag by name hash.
Object* object::find_child(Object& obj, ObjectNameHash const name_hash) {
	return const_cast<Object*>(
		object::find_impl(obj.children, StringRef{}, name_hash)
	);
}

/// Find tag by name hash.
Object const* object::find_child(Object const& obj, ObjectNameHash const name_hash) {
	return object::find_impl(obj.children, StringRef{}, name_hash);
}

} // namespace quanta
<|endoftext|>


<commit_before>// Copyright (c) 2010 Hewlett-Packard Development Company, L.P. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chromix/Chromix.h"
#include "chromix/ChromixExtension.h"
#include "chromix/WebKitClientImpl.h"

#include <base/message_loop.h>
#include <base/at_exit.h>
#include <base/command_line.h>
#include <third_party/WebKit/WebKit/chromium/public/WebKit.h>
#include <third_party/WebKit/WebKit/chromium/public/WebRuntimeFeatures.h>
#include <third_party/WebKit/WebKit/chromium/public/WebScriptController.h>
#if defined(OS_MACOSX)
#include <third_party/WebKit/WebKit/mac/WebCoreSupport/WebSystemInterface.h>
#elif defined(OS_LINUX)
#include <app/app_switches.h>
#include <app/gfx/gl/gl_implementation.h>
#include <gfx/gtk_util.h>
#endif

namespace Chromix {

class MixKit {
public:
    // webKitClient depends on state initialized by messageLoop
    MixKit(int argc, const char* argv[]) :
        atExitManager(),
        messageLoop(),
        webKitClient()
    {
        CommandLine::Init(argc, argv);

        WebKit::initialize(&webKitClient);

#if defined(OS_MACOSX)
        InitWebCoreSystemInterface();
#elif defined(OS_LINUX)
        //XXX should we use defined(TOOLKIT_USES_GTK) ?
        // Check if --use-gl specified
        // If 'osmesa' specified, then explicitly initialize it and don't initialize
        // X11. If we don't explicitly initialize it then we will crash
        // because the desktop GL will be tried first and we won't have an X connection.
        // For anything other than 'osmesa', initialize an X connection
        // and let the default GL initialization happen.
        // For 'osmesa', libosmesa.so from the build needs to be in the same directory
        // as the hosting application.
        CommandLine* commandLine = CommandLine::ForCurrentProcess();
        gfx::GLImplementation requestedGLImplementation = gfx::kGLImplementationNone;
        if (commandLine->HasSwitch(switches::kUseGL)) {
            std::string requestedGLName = commandLine->GetSwitchValueASCII(switches::kUseGL);
            // Can't use this, so compare manually.
            //XXX http://code.google.com/p/chromium/issues/detail?id=67001
            //requestedGLImplementation = gfx::GetNamedGLImplementation(requestedGLName);
            if (requestedGLName == gfx::kGLImplementationOSMesaName)
                requestedGLImplementation = gfx::kGLImplementationOSMesaGL;
        }
        if (requestedGLImplementation == gfx::kGLImplementationOSMesaGL)
            gfx::InitializeGLBindings(gfx::kGLImplementationOSMesaGL);
        else {
            g_thread_init(NULL);
            gfx::GtkInitFromCommandLine(*commandLine);
        }
#endif

        WebKit::WebRuntimeFeatures::enableWebGL(true);

        WebKit::WebScriptController::enableV8SingleThreadMode();
        WebKit::WebScriptController::registerExtension(Chromix::ChromixExtension::Get());
    }

    ~MixKit() {
        WebKit::shutdown();
    }

private:
    // These setup global state - must be initialized before WebKitClient
    base::AtExitManager atExitManager;
    MessageLoop messageLoop;
    Chromix::WebKitClientImpl webKitClient;

    DISALLOW_COPY_AND_ASSIGN(MixKit);
};

static MixKit* mixKit = 0;

InitializeResult initialize(int argc, const char* argv[]) {
    InitializeResult result = InitializeAlready;
    if (!mixKit) {
        mixKit = new MixKit(argc, argv);
        result = mixKit ? InitializeSuccess : InitializeFailure;
    }
    return result;
}

void shutdown() {
    delete mixKit;
    mixKit = 0;
}

}
<commit_msg>Shutdown resource loader thread when MixKit shutdown.<commit_after>// Copyright (c) 2010 Hewlett-Packard Development Company, L.P. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chromix/Chromix.h"
#include "chromix/ChromixExtension.h"
#include "chromix/WebKitClientImpl.h"

#include <base/message_loop.h>
#include <base/at_exit.h>
#include <base/command_line.h>
#include <third_party/WebKit/WebKit/chromium/public/WebKit.h>
#include <third_party/WebKit/WebKit/chromium/public/WebRuntimeFeatures.h>
#include <third_party/WebKit/WebKit/chromium/public/WebScriptController.h>
#include <webkit/tools/test_shell/simple_resource_loader_bridge.h>
#if defined(OS_MACOSX)
#include <third_party/WebKit/WebKit/mac/WebCoreSupport/WebSystemInterface.h>
#elif defined(OS_LINUX)
#include <app/app_switches.h>
#include <app/gfx/gl/gl_implementation.h>
#include <gfx/gtk_util.h>
#endif

namespace Chromix {

class MixKit {
public:
    // webKitClient depends on state initialized by messageLoop
    MixKit(int argc, const char* argv[]) :
        atExitManager(),
        messageLoop(),
        webKitClient()
    {
        CommandLine::Init(argc, argv);

        WebKit::initialize(&webKitClient);

#if defined(OS_MACOSX)
        InitWebCoreSystemInterface();
#elif defined(OS_LINUX)
        //XXX should we use defined(TOOLKIT_USES_GTK) ?
        // Check if --use-gl specified
        // If 'osmesa' specified, then explicitly initialize it and don't initialize
        // X11. If we don't explicitly initialize it then we will crash
        // because the desktop GL will be tried first and we won't have an X connection.
        // For anything other than 'osmesa', initialize an X connection
        // and let the default GL initialization happen.
        // For 'osmesa', libosmesa.so from the build needs to be in the same directory
        // as the hosting application.
        CommandLine* commandLine = CommandLine::ForCurrentProcess();
        gfx::GLImplementation requestedGLImplementation = gfx::kGLImplementationNone;
        if (commandLine->HasSwitch(switches::kUseGL)) {
            std::string requestedGLName = commandLine->GetSwitchValueASCII(switches::kUseGL);
            // Can't use this, so compare manually.
            //XXX http://code.google.com/p/chromium/issues/detail?id=67001
            //requestedGLImplementation = gfx::GetNamedGLImplementation(requestedGLName);
            if (requestedGLName == gfx::kGLImplementationOSMesaName)
                requestedGLImplementation = gfx::kGLImplementationOSMesaGL;
        }
        if (requestedGLImplementation == gfx::kGLImplementationOSMesaGL)
            gfx::InitializeGLBindings(gfx::kGLImplementationOSMesaGL);
        else {
            g_thread_init(NULL);
            gfx::GtkInitFromCommandLine(*commandLine);
        }
#endif

        WebKit::WebRuntimeFeatures::enableWebGL(true);

        WebKit::WebScriptController::enableV8SingleThreadMode();
        WebKit::WebScriptController::registerExtension(Chromix::ChromixExtension::Get());
    }

    ~MixKit() {
        // webkit_glue::ResourceLoaderBridge::Create() creates a SimpleResourceLoaderBridge
        // which spawns an IO thread - so need to shut it down.
        SimpleResourceLoaderBridge::Shutdown();
        WebKit::shutdown();
    }

private:
    // These setup global state - must be initialized before WebKitClient
    base::AtExitManager atExitManager;
    MessageLoop messageLoop;
    Chromix::WebKitClientImpl webKitClient;

    DISALLOW_COPY_AND_ASSIGN(MixKit);
};

static MixKit* mixKit = 0;

InitializeResult initialize(int argc, const char* argv[]) {
    InitializeResult result = InitializeAlready;
    if (!mixKit) {
        mixKit = new MixKit(argc, argv);
        result = mixKit ? InitializeSuccess : InitializeFailure;
    }
    return result;
}

void shutdown() {
    delete mixKit;
    mixKit = 0;
}

}
<|endoftext|>


<commit_before>/*

  SjASMPlus Z80 Cross Compiler

  Copyright (c) 2004-2006 Aprisobal

  This software is provided 'as-is', without any express or implied warranty.
  In no event will the authors be held liable for any damages arising from the
  use of this software.

  Permission is granted to anyone to use this software for any purpose,
  including commercial applications, and to alter it and redistribute it freely,
  subject to the following restrictions:

  1. The origin of this software must not be misrepresented; you must not claim
	 that you wrote the original software. If you use this software in a product,
	 an acknowledgment in the product documentation would be appreciated but is
	 not required.

  2. Altered source versions must be plainly marked as such, and must not be
	 misrepresented as being the original software.

  3. This notice may not be removed or altered from any source distribution.

*/

// io_trd.cpp

#include "sjdefs.h"

static int saveEmptyWrite(FILE* ff, byte* buf, const char label[8]) {
	int i;
	//catalog (8 zeroed sectors)
	if (fwrite(buf, 1, 1024, ff) < 1024) return 0;
	if (fwrite(buf, 1, 1024, ff) < 1024) return 0;
	// disc info in sector 8
	buf[0xe1] = 0;		// first free sector
	buf[0xe2] = 1;		// track of first free sector
	buf[0xe3] = 0x16;	// disk type (80 tracks, double sided)
	buf[0xe4] = 0;		// number of used catalog entries (including deleted!) (number of files)
	buf[0xe5] = 0xf0;	// number of free sectors (WORD)
	buf[0xe6] = 0x09;	// ^^
	buf[0xe7] = 0x10;	// TR-DOS ID (0x10)
	buf[0xe8] = buf[0xe9] = 0;		// unused WORD = 0
	for (i = 0; i < 9; ++i) buf[0xea + i] = 0x20;	// spaces fill or disk protecting password
	buf[0xf3] = 0;		// unused = 0
	buf[0xf4] = 0;		// number of deleted files on disk
	// disc label + end of disk info (8x 20h, 3x 0)
	for (i = 0; i < 8; ++i) buf[0xf5 + i] = (nullptr != label) ? label[i] : 0x20;
	if (fwrite(buf, 1, 256, ff) < 256) return 0;
	for (i = 0xe1; i < 0x100; ++i) buf[i] = 0;		// clear the buffer back to all zeroes
	// remaining sectors in image contains all zeroes
	if (fwrite(buf, 1, 768, ff) < 768) return 0;
	for (i = 0; i < 640 - 3; i++) {
		if (fwrite(buf, 1, 1024, ff) < 1024) return 0;
	}
	return 1;
}

int TRD_SaveEmpty(char* fname, const char label[8]) {
	FILE* ff;
	if (!FOPEN_ISOK(ff, fname, "wb")) {
		Error("Error opening file", fname, IF_FIRST);
		return 0;
	}
	byte* buf = (byte*) calloc(1024, sizeof(byte));
	if (buf == NULL) ErrorOOM();
	int result = saveEmptyWrite(ff, buf, label);
	free(buf);
	fclose(ff);
	if (!result) Error("Write error (disk full?)", fname, IF_FIRST);
	return result;
}

// use autostart == -1 to disable it (the valid autostart is 0..9999 as line number of BASIC program)
int TRD_AddFile(char* fname, char* fhobname, int start, int length, int autostart, bool replace) {
	// do some preliminary checks with file name and autostart
	size_t hobNameL = strlen(fhobname), fatPos;
	char extLetter = 0;
	if (hobNameL > 1) {
		char* ext = strrchr(fhobname, '.');
		if (ext) {
			extLetter = ext[1];
			hobNameL = ext - fhobname;
		}
	}
	byte hobnamebin[9];			// prepare binary format of name (as on disc) (for replace search)
	memset(hobnamebin,' ',9);
	if (extLetter) hobnamebin[8] = extLetter;
	memcpy(hobnamebin, fhobname, std::min(hobNameL, size_t(8)));	// binary form is 8+1 with spaces-padding
	if (0 <= autostart && ('B' != extLetter || 9999 < autostart)) {
		Warning("zx.trdimage_add_file: autostart value is BASIC program line number (0..9999) (in lua use -1 otherwise).");
		autostart = -1;
	}
	if (warningNotSuppressed()) {
		switch (extLetter) {
			case 'B': case 'C': case 'D': case '#': break;
			default:
				Warning("zx.trdimage_add_file: invalid file extension, TRDOS extensions are B, C, D and #.", fhobname);
		}
	}
	// more validations - for Lua (or SAVETRD letting wrong values go through)
	const int secsLength = (length + 255 + (0 <= autostart ? 4 : 0))>>8;
	if (!DeviceID) {
		Error("zx.trdimage_add_file: this function available only in real device emulation mode.");
		return 0;
	}
	if (start < 0 || 0xFFFF < start) {
		Error("zx.trdimage_add_file: start address must be in 0000..FFFF range", bp, PASS3);
		return 0;
	}
	if (length <= 0 || 0xFF00 < length) {
		// zero length not allowed any more, because TRD docs on internet are imprecise
		// and I'm not sure what is the correct way of saving zero length file => error
		Error("zx.trdimage_add_file: length must be in 0001..FF00 range", bp, PASS3);
		return 0;
	}
	if (0x10000 < start+length) {
		Error("zx.trdimage_add_file: provided start+length will run out of device memory", bp, PASS3);
		return 0;
	}
	if (0xFF < secsLength) {
		Error("zx.trdimage_add_file: sector length over 0xFF max", bp, PASS3);
		return 0;
	}

	FILE* ff;
	if (!FOPEN_ISOK(ff, fname, "r+b")) Error("Error opening file", fname, FATAL);

	byte hdr[16], trd[31];
	if (fseek(ff, 0x8e1, SEEK_SET)) {
		Error("TRD image has wrong format", fname, IF_FIRST); return 0;
	}
	if (31UL != fread(trd, 1, 31, ff) && 0x10 != trd[6]) {	// verify also TR-DOS ID
		Error("TRD image read error", fname, IF_FIRST); return 0;
	}
	constexpr size_t FAT_END_POS = 128*16;
	int freeSecs = trd[4] + (trd[5] << 8);

	// "replace" feature, goes through whole FAT and deletes all files with identical name
	// In special case one of the files connects to current first free sector the disc space
	// will be recovered, but overall this feature is very primitive (not defragging fat or disc)
	if (replace) {
		bool discInfoModified = false;
		fseek(ff, 0, SEEK_SET);
		for (fatPos = 0; fatPos < FAT_END_POS; fatPos += 16) {
			if (16UL != fread(hdr, 1, 16, ff)) {
				Error("Read error", fname, IF_FIRST); return 0;
			}
			if (0 == hdr[0]) break;		// beyond last FAT record, finish the loop
			if (memcmp(hdr, hobnamebin, 9)) continue;	// different file name -> continue
			discInfoModified = true;
			const bool isLastFile = ((fatPos>>4) + 1) == trd[3];
			if (isLastFile) {
				// It's last file of catalog, erase it as if it was not on disc at all
				// verify if the free space starts just where last file ends (integrity of TRD image)
				const int secsLengthDel = hdr[0x0d];
				const byte nextTrack = ((secsLengthDel+hdr[0x0e])>>4) + hdr[0x0f];
				const byte nextSector = (secsLengthDel+hdr[0x0e])&0x0F;
				// if file connects to first free sector, salvage the space back
				if (nextSector != trd[0] || nextTrack != trd[1]) {
					Error("TRD free sector was not connected to last file", fname, IF_FIRST); return 0;
				}
				// return the sectors used by file back to "free sectors" pool
				trd[0] = hdr[0x0e];
				trd[1] = hdr[0x0f];
				freeSecs += secsLengthDel;
				// delete the file (wipe catalog entry completely as if it was not written)
				--trd[3];
				hdr[0] = 0;
			} else {
				// delete the file (but it stays in catalog as deleted file)
				hdr[0] = 1;
				++trd[19];
			}
			// write modified FAT entry
			if (fseek(ff, fatPos, SEEK_SET)) {
				Error("TRD image has wrong format", fname, IF_FIRST); return 0;
			}
			if (16UL != fwrite(hdr, 1, 16, ff)) {
				Error("FAT write error", fname, IF_FIRST); return 0;
			}
		}
		// if some files were deleted, update disc info sector too to make image "valid" before writing file
		if (discInfoModified) {
			// update remaining free sectors
			trd[4] = (unsigned char)(freeSecs & 0xff);
			trd[5] = (unsigned char)(freeSecs >> 8);
			if (fseek(ff, 0x8e1, SEEK_SET)) {
				Error("TRD image has wrong format", fname, IF_FIRST); return 0;
			}
			if (31UL != fwrite(trd, 1, 31, ff)) {
				Error("Disc info write error", fname, IF_FIRST); return 0;
			}
		}
	}

	if (freeSecs < secsLength) {
		Error("TRD image has not enough free space", fname, IF_FIRST); return 0;
	}

	// Use the last catalog position and verify it's free
	fatPos = size_t(trd[3]) * 16;
	if (FAT_END_POS <= fatPos) {
		Error("TRD image is full of files", fname, IF_FIRST); return 0;
	}
	fseek(ff, fatPos, SEEK_SET);
	if (16UL != fread(hdr, 1, 16, ff)) {
		Error("Read error", fname, IF_FIRST); return 0;
	}
	if (hdr[0] != 0) {
		Error("TRD inconsistent catalog data", fname, IF_FIRST); return 0;
	}

	// save the file content first
	if (fseek(ff, (trd[1] << 12) + (trd[0] << 8), SEEK_SET)) {
		Error("TRD image has wrong format", fname, IF_FIRST); return 0;
	}

	SaveRAM(ff, start, length);

	if (0 <= autostart) {
		byte abin[] {0x80, 0xAA, static_cast<byte>(autostart), static_cast<byte>(autostart>>8)};
		if (4 != fwrite(abin, 1, 4, ff)) {
			Error("Write error", fname, IF_FIRST);
			return 0;
		}
	}

	//header of file
	memcpy(hdr, hobnamebin, 9);
	if ('B' == extLetter)	{
		hdr[0x09] = (unsigned char)(length & 0xff);
		hdr[0x0a] = (unsigned char)(length >> 8);
	} else	{
		hdr[0x09] = (unsigned char)(start & 0xff);
		hdr[0x0a] = (unsigned char)(start >> 8);
	}
	hdr[0x0b] = (unsigned char)(length & 0xff);
	hdr[0x0c] = (unsigned char)(length >> 8);
	hdr[0x0d] = secsLength;
	hdr[0x0e] = trd[0];
	hdr[0x0f] = trd[1];

	if (fseek(ff, fatPos, SEEK_SET)) {
		Error("TRD image has wrong format", fname, IF_FIRST); return 0;
	}
	if (16UL != fwrite(hdr, 1, 16, ff)) {
		Error("TRD FAT Write error (file damaged)", fname, IF_FIRST); return 0;
	}

	// update next free sector/track position
	trd[1] += (secsLength+trd[0])>>4;
	trd[0] = (secsLength+trd[0])&0x0F;
	// update remaining free sectors
	freeSecs -= secsLength;
	trd[4] = (unsigned char)(freeSecs & 0xff);
	trd[5] = (unsigned char)(freeSecs >> 8);
	++trd[3];		// count of total files (including deleted)
	// write disc info
	if (fseek(ff, 0x8e1, SEEK_SET)) {
		Error("TRD image has wrong format", fname, IF_FIRST); return 0;
	}
	if (31UL != fwrite(trd, 1, 31, ff)) {
		Error("Disc info write error", fname, IF_FIRST); return 0;
	}

	fclose(ff);
	return 1;
}
<commit_msg>tests vs TRD: hunting down issue with savetrd2 test failing on windows<commit_after>/*

  SjASMPlus Z80 Cross Compiler

  Copyright (c) 2004-2006 Aprisobal

  This software is provided 'as-is', without any express or implied warranty.
  In no event will the authors be held liable for any damages arising from the
  use of this software.

  Permission is granted to anyone to use this software for any purpose,
  including commercial applications, and to alter it and redistribute it freely,
  subject to the following restrictions:

  1. The origin of this software must not be misrepresented; you must not claim
	 that you wrote the original software. If you use this software in a product,
	 an acknowledgment in the product documentation would be appreciated but is
	 not required.

  2. Altered source versions must be plainly marked as such, and must not be
	 misrepresented as being the original software.

  3. This notice may not be removed or altered from any source distribution.

*/

// io_trd.cpp

#include "sjdefs.h"

static int saveEmptyWrite(FILE* ff, byte* buf, const char label[8]) {
	int i;
	//catalog (8 zeroed sectors)
	if (fwrite(buf, 1, 1024, ff) < 1024) return 0;
	if (fwrite(buf, 1, 1024, ff) < 1024) return 0;
	// disc info in sector 8
	buf[0xe1] = 0;		// first free sector
	buf[0xe2] = 1;		// track of first free sector
	buf[0xe3] = 0x16;	// disk type (80 tracks, double sided)
	buf[0xe4] = 0;		// number of used catalog entries (including deleted!) (number of files)
	buf[0xe5] = 0xf0;	// number of free sectors (WORD)
	buf[0xe6] = 0x09;	// ^^
	buf[0xe7] = 0x10;	// TR-DOS ID (0x10)
	buf[0xe8] = buf[0xe9] = 0;		// unused WORD = 0
	for (i = 0; i < 9; ++i) buf[0xea + i] = 0x20;	// spaces fill or disk protecting password
	buf[0xf3] = 0;		// unused = 0
	buf[0xf4] = 0;		// number of deleted files on disk
	// disc label + end of disk info (8x 20h, 3x 0)
	for (i = 0; i < 8; ++i) buf[0xf5 + i] = (nullptr != label) ? label[i] : 0x20;
	if (fwrite(buf, 1, 256, ff) < 256) return 0;
	for (i = 0xe1; i < 0x100; ++i) buf[i] = 0;		// clear the buffer back to all zeroes
	// remaining sectors in image contains all zeroes
	if (fwrite(buf, 1, 768, ff) < 768) return 0;
	for (i = 0; i < 640 - 3; i++) {
		if (fwrite(buf, 1, 1024, ff) < 1024) return 0;
	}
	return 1;
}

int TRD_SaveEmpty(char* fname, const char label[8]) {
	FILE* ff;
	if (!FOPEN_ISOK(ff, fname, "wb")) {
		Error("Error opening file", fname, IF_FIRST);
		return 0;
	}
	byte* buf = (byte*) calloc(1024, sizeof(byte));
	if (buf == NULL) ErrorOOM();
	int result = saveEmptyWrite(ff, buf, label);
	free(buf);
	fclose(ff);
	if (!result) Error("Write error (disk full?)", fname, IF_FIRST);
	return result;
}

// use autostart == -1 to disable it (the valid autostart is 0..9999 as line number of BASIC program)
int TRD_AddFile(char* fname, char* fhobname, int start, int length, int autostart, bool replace) {
	// do some preliminary checks with file name and autostart
	size_t hobNameL = strlen(fhobname), fatPos;
	char extLetter = 0;
	if (hobNameL > 1) {
		char* ext = strrchr(fhobname, '.');
		if (ext) {
			extLetter = ext[1];
			hobNameL = ext - fhobname;
		}
	}
	byte hobnamebin[9];			// prepare binary format of name (as on disc) (for replace search)
	memset(hobnamebin,' ',9);
	if (extLetter) hobnamebin[8] = extLetter;
	memcpy(hobnamebin, fhobname, std::min(hobNameL, size_t(8)));	// binary form is 8+1 with spaces-padding
	if (0 <= autostart && ('B' != extLetter || 9999 < autostart)) {
		Warning("zx.trdimage_add_file: autostart value is BASIC program line number (0..9999) (in lua use -1 otherwise).");
		autostart = -1;
	}
	if (warningNotSuppressed()) {
		switch (extLetter) {
			case 'B': case 'C': case 'D': case '#': break;
			default:
				Warning("zx.trdimage_add_file: invalid file extension, TRDOS extensions are B, C, D and #.", fhobname);
		}
	}
	// more validations - for Lua (or SAVETRD letting wrong values go through)
	const int secsLength = (length + 255 + (0 <= autostart ? 4 : 0))>>8;
	if (!DeviceID) {
		Error("zx.trdimage_add_file: this function available only in real device emulation mode.");
		return 0;
	}
	if (start < 0 || 0xFFFF < start) {
		Error("zx.trdimage_add_file: start address must be in 0000..FFFF range", bp, PASS3);
		return 0;
	}
	if (length <= 0 || 0xFF00 < length) {
		// zero length not allowed any more, because TRD docs on internet are imprecise
		// and I'm not sure what is the correct way of saving zero length file => error
		Error("zx.trdimage_add_file: length must be in 0001..FF00 range", bp, PASS3);
		return 0;
	}
	if (0x10000 < start+length) {
		Error("zx.trdimage_add_file: provided start+length will run out of device memory", bp, PASS3);
		return 0;
	}
	if (0xFF < secsLength) {
		Error("zx.trdimage_add_file: sector length over 0xFF max", bp, PASS3);
		return 0;
	}

	FILE* ff;
	if (!FOPEN_ISOK(ff, fname, "r+b")) Error("Error opening file", fname, FATAL);

	byte hdr[16], trd[31];
	if (fseek(ff, 0x8e1, SEEK_SET)) {
		Error("TRD image has wrong format", fname, IF_FIRST); return 0;
	}
	if (31UL != fread(trd, 1, 31, ff) && 0x10 != trd[6]) {	// verify also TR-DOS ID
		Error("TRD image read error", fname, IF_FIRST); return 0;
	}
	constexpr size_t FAT_END_POS = 128*16;
	int freeSecs = trd[4] + (trd[5] << 8);

	// "replace" feature, goes through whole FAT and deletes all files with identical name
	// In special case one of the files connects to current first free sector the disc space
	// will be recovered, but overall this feature is very primitive (not defragging fat or disc)
	if (replace) {
		bool discInfoModified = false;
		fseek(ff, 0, SEEK_SET);
		for (fatPos = 0; fatPos < FAT_END_POS; fatPos += 16) {
			if (16UL != fread(hdr, 1, 16, ff)) {
				Error("Read error", fname, IF_FIRST); return 0;
			}
			if (0 == hdr[0]) break;		// beyond last FAT record, finish the loop
			if (memcmp(hdr, hobnamebin, 9)) continue;	// different file name -> continue
			discInfoModified = true;
			const bool isLastFile = ((fatPos>>4) + 1) == trd[3];
			if (isLastFile) {
				// It's last file of catalog, erase it as if it was not on disc at all
				// verify if the free space starts just where last file ends (integrity of TRD image)
				const int secsLengthDel = hdr[0x0d];
				const byte nextTrack = ((secsLengthDel+hdr[0x0e])>>4) + hdr[0x0f];
				const byte nextSector = (secsLengthDel+hdr[0x0e])&0x0F;
				// if file connects to first free sector, salvage the space back
				if (nextSector != trd[0] || nextTrack != trd[1]) {
					Error("TRD free sector was not connected to last file", fname, IF_FIRST); return 0;
				}
				// return the sectors used by file back to "free sectors" pool
				trd[0] = hdr[0x0e];
				trd[1] = hdr[0x0f];
				freeSecs += secsLengthDel;
				// delete the file (wipe catalog entry completely as if it was not written)
				--trd[3];
				hdr[0] = 0;
			} else {
				// delete the file (but it stays in catalog as deleted file)
				hdr[0] = 1;
				++trd[19];
			}
			// write modified FAT entry
			if (fseek(ff, fatPos, SEEK_SET)) {
				Error("TRD image has wrong format", fname, IF_FIRST); return 0;
			}
			if (16UL != fwrite(hdr, 1, 16, ff)) {
				Error("FAT write error", fname, IF_FIRST); return 0;
			}
		}
		// if some files were deleted, update disc info sector too to make image "valid" before writing file
		if (discInfoModified) {
			// update remaining free sectors
			trd[4] = (unsigned char)(freeSecs & 0xff);
			trd[5] = (unsigned char)(freeSecs >> 8);
			if (fseek(ff, 0x8e1, SEEK_SET)) {
				Error("TRD image has wrong format", fname, IF_FIRST); return 0;
			}
			if (31UL != fwrite(trd, 1, 31, ff)) {
				Error("Disc info write error", fname, IF_FIRST); return 0;
			}
		}
	}

	if (freeSecs < secsLength) {
		Error("TRD image has not enough free space", fname, IF_FIRST); return 0;
	}

	// Use the last catalog position and verify it's free
	fatPos = size_t(trd[3]) * 16;
	if (FAT_END_POS <= fatPos) {
		Error("TRD image is full of files", fname, IF_FIRST); return 0;
	}
	fseek(ff, fatPos, SEEK_SET);
	if (16UL != fread(hdr, 1, 16, ff)) {
		Error("Read error", fname, IF_FIRST); return 0;
	}
	if (hdr[0] != 0) {
		Error("TRD inconsistent catalog data", fname, IF_FIRST); return 0;
	}

	//TODO debug - remove
	{
		printf("DEBUG SAVETRD: [%s] [%s] %d %d\n", fname, fhobname, start, length);
		byte dbg_buffer[128];
		fseek(ff, 0, SEEK_SET);
		if (128UL != fread(dbg_buffer, 1, 128, ff)) Error("Read error", fname, IF_FIRST);
		for (int ii = 0; ii < 128; ii+=16) {
			printf("0x%02X:", ii);
			for (int jj = ii; jj < ii+16; ++jj) {
				printf(" %02X", dbg_buffer[jj]);
			}
			printf("\n");
		}
	}

	// save the file content first
	if (fseek(ff, (long(trd[1]) << 12) + (long(trd[0]) << 8), SEEK_SET)) {
		Error("TRD image has wrong format", fname, IF_FIRST); return 0;
	}

	SaveRAM(ff, start, length);

	if (0 <= autostart) {
		byte abin[] {0x80, 0xAA, static_cast<byte>(autostart), static_cast<byte>(autostart>>8)};
		if (4 != fwrite(abin, 1, 4, ff)) {
			Error("Write error", fname, IF_FIRST);
			return 0;
		}
	}

	//header of file
	memcpy(hdr, hobnamebin, 9);
	if ('B' == extLetter)	{
		hdr[0x09] = (unsigned char)(length & 0xff);
		hdr[0x0a] = (unsigned char)(length >> 8);
	} else	{
		hdr[0x09] = (unsigned char)(start & 0xff);
		hdr[0x0a] = (unsigned char)(start >> 8);
	}
	hdr[0x0b] = (unsigned char)(length & 0xff);
	hdr[0x0c] = (unsigned char)(length >> 8);
	hdr[0x0d] = secsLength;
	hdr[0x0e] = trd[0];
	hdr[0x0f] = trd[1];

	if (fseek(ff, fatPos, SEEK_SET)) {
		Error("TRD image has wrong format", fname, IF_FIRST); return 0;
	}
	if (16UL != fwrite(hdr, 1, 16, ff)) {
		Error("TRD FAT Write error (file damaged)", fname, IF_FIRST); return 0;
	}

	// update next free sector/track position
	trd[1] += (secsLength+trd[0])>>4;
	trd[0] = (secsLength+trd[0])&0x0F;
	// update remaining free sectors
	freeSecs -= secsLength;
	trd[4] = (unsigned char)(freeSecs & 0xff);
	trd[5] = (unsigned char)(freeSecs >> 8);
	++trd[3];		// count of total files (including deleted)
	// write disc info
	if (fseek(ff, 0x8e1, SEEK_SET)) {
		Error("TRD image has wrong format", fname, IF_FIRST); return 0;
	}
	if (31UL != fwrite(trd, 1, 31, ff)) {
		Error("Disc info write error", fname, IF_FIRST); return 0;
	}

	fclose(ff);
	return 1;
}
<|endoftext|>


<commit_before>#include <stdlib.h>
#include <stdio.h>
#include <string>
#include <unistd.h>
#include <iostream>
#include <gflags/gflags.h>
#include <set>
#include "sort_file.h"
#include "logging.h"
#include "filesystem.h"
#include "common/tools_util.h"

DEFINE_int32(total, 0, "total numbers of map tasks");
DEFINE_int32(reduce_no, 0, "the reduce number of this reduce task");
DEFINE_string(work_dir, "/tmp", "the shuffle work dir");
DEFINE_int32(batch, 100, "merge how many maps output at the same time");
DEFINE_int32(attempt_id, 0, "the attempt_id of this reduce task");

using baidu::common::Log;
using baidu::common::FATAL;
using baidu::common::INFO;
using baidu::common::WARNING;
using namespace baidu::shuttle;

std::set<std::string> g_merged;
int32_t g_file_no(0);
FileSystem* g_fs(NULL);

void CollectFilesToMerge(std::vector<std::string>* maps_to_merge) {
    assert(maps_to_merge);
    std::vector<std::string> children;
    bool ok = g_fs->List(FLAGS_work_dir, &children);
    if (!ok) {
        return;
    }
    std::vector<std::string>::iterator it;
    for (it = children.begin(); it != children.end(); it++) {
        const std::string& file_name = *it;
        if (file_name.find("_temporary/shuffle/map_") != std::string::npos &&
            g_merged.find(file_name) == g_merged.end()) {
            LOG(INFO, "maps_to_merge: %s", file_name.c_str());
            maps_to_merge->push_back(file_name);
        }
    }
}

void MergeMapOutput(const std::vector<std::string>& maps_to_merge) {
    std::vector<std::string> file_names;
    std::vector<std::string>::const_iterator it;
    std::vector<std::string>::iterator jt;
    std::vector<std::string> real_merged_maps;
    int map_ct = 0;
    for (it = maps_to_merge.begin(); it != maps_to_merge.end(); it++) {
        const std::string& map_dir = *it;
        std::vector<std::string> sort_files;
        if (g_fs->List(map_dir, &sort_files)) {
            for (jt = sort_files.begin(); jt != sort_files.end(); jt++) {
                const std::string& file_name = *jt;
                if (file_name.find(".sort") != std::string::npos) {
                    file_names.push_back(file_name);
                }
            }
        } else {
            LOG(FATAL, "fail to list %s", map_dir.c_str());
        }
        map_ct++;
        real_merged_maps.push_back(map_dir);
        if (map_ct >= FLAGS_batch){
            break;
        }
    }
    if (file_names.empty()) {
        LOG(WARNING, "not map output found");
        return;
    }
    MergeFileReader reader;
    FileSystem::Param param;
    Status status = reader.Open(file_names, param, kHdfsFile);
    if (status != kOk) {
        LOG(FATAL, "fail to open: %s", reader.GetErrorFile().c_str());
    }
    char s_reduce_no[256];
    snprintf(s_reduce_no, sizeof(s_reduce_no), "%05d", FLAGS_reduce_no);
    std::string s_reduce_key(s_reduce_no);

    SortFileReader::Iterator* scan_it = reader.Scan(s_reduce_key,
                                                     s_reduce_key + "\xff");
    if (scan_it->Error() != kOk && scan_it->Error() != kNoMore) {
        LOG(FATAL, "fail to scan: %s", reader.GetErrorFile().c_str());
    }

    char output_file[4096];
    snprintf(output_file, sizeof(output_file), "%s/reduce_%d_%d/%d.sort",
             FLAGS_work_dir.c_str(), FLAGS_reduce_no, FLAGS_attempt_id,
             g_file_no++);
    SortFileWriter * writer = SortFileWriter::Create(kHdfsFile, &status);
    if (status != kOk) {
        LOG(FATAL, "fail to create writer");
    }
    FileSystem::Param param_write;
    param_write["replica"] = "2";
    status = writer->Open(output_file, param_write);
    if (status != kOk) {
        LOG(FATAL, "fail to open %s for write", output_file);
    }
    while (!scan_it->Done()) {
        status = writer->Put(scan_it->Key(), scan_it->Value());
        if (status != kOk) {
            LOG(FATAL, "fail to put: %s", output_file);
        }
        scan_it->Next();
    }
    if (scan_it->Error() != kOk && scan_it->Error() != kNoMore) {
        LOG(FATAL, "fail to scan: %s", reader.GetErrorFile().c_str());
    }
    status = writer->Close();
    if (status != kOk) {
        LOG(FATAL, "fail to close: %s", output_file);
    }
    reader.Close();
    delete writer;
    for (it = real_merged_maps.begin(); it != real_merged_maps.end(); it++) {
        LOG(INFO, "g_merged insert: %s", it->c_str());
        g_merged.insert(*it);
    }
}

void MergeAndPrint() {
    char reduce_merge_dir[4096];
    snprintf(reduce_merge_dir, sizeof(reduce_merge_dir),
             "%s/reduce_%d_%d",
             FLAGS_work_dir.c_str(), FLAGS_reduce_no, FLAGS_attempt_id);
    std::vector<std::string> children;
    if (!g_fs->List(reduce_merge_dir, &children)) {
        LOG(FATAL, "fail to list: %s", reduce_merge_dir);
    }
    std::vector<std::string> file_names;
    std::vector<std::string>::iterator it;
    for (it = children.begin(); it != children.end(); it++) {
        const std::string& file_name = *it;
        if (file_name.find(".sort") != std::string::npos) {
            file_names.push_back(file_name);
        }
    }
    if (file_names.empty()) {
        LOG(WARNING, "no data for this reduce task");
        return;
    }
    MergeFileReader reader;
    FileSystem::Param param;
    Status status = reader.Open(file_names, param, kHdfsFile);
    if (status != kOk) {
        LOG(FATAL, "fail to open: %s", reader.GetErrorFile().c_str());
    }
    SortFileReader::Iterator* scan_it = reader.Scan("", "");
    if (scan_it->Error() != kOk && scan_it->Error() != kNoMore) {
        LOG(FATAL, "fail to scan: %s", reader.GetErrorFile().c_str());
    }
    while (!scan_it->Done()) {
        std::cout << scan_it->Value() << std::endl;
        scan_it->Next();
    }
    if (scan_it->Error() != kOk && scan_it->Error() != kNoMore) {
        LOG(FATAL, "fail to scan: %s", reader.GetErrorFile().c_str());
    }
    reader.Close();
}

int main(int argc, char* argv[]) {
    baidu::common::SetLogFile(GetLogName("./shuffle_tool.log").c_str());
    baidu::common::SetWarningFile(GetLogName("./shuffle_tool.log.wf").c_str());
    google::ParseCommandLineFlags(&argc, &argv, true);
    FileSystem::Param param;
    g_fs = FileSystem::CreateInfHdfs(param);

    while ((int32_t)g_merged.size() < FLAGS_total) {
        std::vector<std::string> maps_to_merge;
        CollectFilesToMerge(&maps_to_merge);
        if (maps_to_merge.size() >= (size_t)FLAGS_batch ||
            maps_to_merge.size() + g_merged.size() >= (size_t)FLAGS_total) {
            MergeMapOutput(maps_to_merge);
        }
        sleep(3);
    }
    MergeAndPrint();
    return 0;
}
<commit_msg>bug fix: valid filename check<commit_after>#include <stdlib.h>
#include <stdio.h>
#include <string>
#include <unistd.h>
#include <iostream>
#include <gflags/gflags.h>
#include <set>
#include <boost/algorithm/string.hpp>
#include "sort_file.h"
#include "logging.h"
#include "filesystem.h"
#include "common/tools_util.h"

DEFINE_int32(total, 0, "total numbers of map tasks");
DEFINE_int32(reduce_no, 0, "the reduce number of this reduce task");
DEFINE_string(work_dir, "/tmp", "the shuffle work dir");
DEFINE_int32(batch, 100, "merge how many maps output at the same time");
DEFINE_int32(attempt_id, 0, "the attempt_id of this reduce task");

using baidu::common::Log;
using baidu::common::FATAL;
using baidu::common::INFO;
using baidu::common::WARNING;
using namespace baidu::shuttle;

std::set<std::string> g_merged;
int32_t g_file_no(0);
FileSystem* g_fs(NULL);

void CollectFilesToMerge(std::vector<std::string>* maps_to_merge) {
    assert(maps_to_merge);
    std::vector<std::string> children;
    bool ok = g_fs->List(FLAGS_work_dir, &children);
    if (!ok) {
        return;
    }
    std::vector<std::string>::iterator it;
    for (it = children.begin(); it != children.end(); it++) {
        const std::string& file_name = *it;
        if (file_name.find("_temporary/shuffle/map_") != std::string::npos &&
            g_merged.find(file_name) == g_merged.end()) {
            LOG(INFO, "maps_to_merge: %s", file_name.c_str());
            maps_to_merge->push_back(file_name);
        }
    }
}

void MergeMapOutput(const std::vector<std::string>& maps_to_merge) {
    std::vector<std::string> file_names;
    std::vector<std::string>::const_iterator it;
    std::vector<std::string>::iterator jt;
    std::vector<std::string> real_merged_maps;
    int map_ct = 0;
    for (it = maps_to_merge.begin(); it != maps_to_merge.end(); it++) {
        const std::string& map_dir = *it;
        std::vector<std::string> sort_files;
        if (g_fs->List(map_dir, &sort_files)) {
            for (jt = sort_files.begin(); jt != sort_files.end(); jt++) {
                const std::string& file_name = *jt;
                if (boost::ends_with(file_name, ".sort")) {
                    file_names.push_back(file_name);
                }
            }
        } else {
            LOG(FATAL, "fail to list %s", map_dir.c_str());
        }
        map_ct++;
        real_merged_maps.push_back(map_dir);
        if (map_ct >= FLAGS_batch){
            break;
        }
    }
    if (file_names.empty()) {
        LOG(WARNING, "not map output found");
        return;
    }
    MergeFileReader reader;
    FileSystem::Param param;
    Status status = reader.Open(file_names, param, kHdfsFile);
    if (status != kOk) {
        LOG(FATAL, "fail to open: %s", reader.GetErrorFile().c_str());
    }
    char s_reduce_no[256];
    snprintf(s_reduce_no, sizeof(s_reduce_no), "%05d", FLAGS_reduce_no);
    std::string s_reduce_key(s_reduce_no);

    SortFileReader::Iterator* scan_it = reader.Scan(s_reduce_key,
                                                     s_reduce_key + "\xff");
    if (scan_it->Error() != kOk && scan_it->Error() != kNoMore) {
        LOG(FATAL, "fail to scan: %s", reader.GetErrorFile().c_str());
    }

    char output_file[4096];
    snprintf(output_file, sizeof(output_file), "%s/reduce_%d_%d/%d.sort",
             FLAGS_work_dir.c_str(), FLAGS_reduce_no, FLAGS_attempt_id,
             g_file_no++);
    SortFileWriter * writer = SortFileWriter::Create(kHdfsFile, &status);
    if (status != kOk) {
        LOG(FATAL, "fail to create writer");
    }
    FileSystem::Param param_write;
    param_write["replica"] = "2";
    status = writer->Open(output_file, param_write);
    if (status != kOk) {
        LOG(FATAL, "fail to open %s for write", output_file);
    }
    while (!scan_it->Done()) {
        status = writer->Put(scan_it->Key(), scan_it->Value());
        if (status != kOk) {
            LOG(FATAL, "fail to put: %s", output_file);
        }
        scan_it->Next();
    }
    if (scan_it->Error() != kOk && scan_it->Error() != kNoMore) {
        LOG(FATAL, "fail to scan: %s", reader.GetErrorFile().c_str());
    }
    status = writer->Close();
    if (status != kOk) {
        LOG(FATAL, "fail to close: %s", output_file);
    }
    reader.Close();
    delete writer;
    for (it = real_merged_maps.begin(); it != real_merged_maps.end(); it++) {
        LOG(INFO, "g_merged insert: %s", it->c_str());
        g_merged.insert(*it);
    }
}

void MergeAndPrint() {
    char reduce_merge_dir[4096];
    snprintf(reduce_merge_dir, sizeof(reduce_merge_dir),
             "%s/reduce_%d_%d",
             FLAGS_work_dir.c_str(), FLAGS_reduce_no, FLAGS_attempt_id);
    std::vector<std::string> children;
    if (!g_fs->List(reduce_merge_dir, &children)) {
        LOG(FATAL, "fail to list: %s", reduce_merge_dir);
    }
    std::vector<std::string> file_names;
    std::vector<std::string>::iterator it;
    for (it = children.begin(); it != children.end(); it++) {
        const std::string& file_name = *it;
        if (boost::ends_with(file_name, ".sort")) {
            file_names.push_back(file_name);
        }
    }
    if (file_names.empty()) {
        LOG(WARNING, "no data for this reduce task");
        return;
    }
    MergeFileReader reader;
    FileSystem::Param param;
    Status status = reader.Open(file_names, param, kHdfsFile);
    if (status != kOk) {
        LOG(FATAL, "fail to open: %s", reader.GetErrorFile().c_str());
    }
    SortFileReader::Iterator* scan_it = reader.Scan("", "");
    if (scan_it->Error() != kOk && scan_it->Error() != kNoMore) {
        LOG(FATAL, "fail to scan: %s", reader.GetErrorFile().c_str());
    }
    while (!scan_it->Done()) {
        std::cout << scan_it->Value() << std::endl;
        scan_it->Next();
    }
    if (scan_it->Error() != kOk && scan_it->Error() != kNoMore) {
        LOG(FATAL, "fail to scan: %s", reader.GetErrorFile().c_str());
    }
    reader.Close();
}

int main(int argc, char* argv[]) {
    baidu::common::SetLogFile(GetLogName("./shuffle_tool.log").c_str());
    baidu::common::SetWarningFile(GetLogName("./shuffle_tool.log.wf").c_str());
    google::ParseCommandLineFlags(&argc, &argv, true);
    FileSystem::Param param;
    g_fs = FileSystem::CreateInfHdfs(param);
    if (FLAGS_total == 0 ) {
        LOG(FATAL, "invalid map task total");
    }
    while ((int32_t)g_merged.size() < FLAGS_total) {
        std::vector<std::string> maps_to_merge;
        CollectFilesToMerge(&maps_to_merge);
        if (maps_to_merge.size() >= (size_t)FLAGS_batch ||
            maps_to_merge.size() + g_merged.size() >= (size_t)FLAGS_total) {
            MergeMapOutput(maps_to_merge);
        }
        sleep(3);
    }
    MergeAndPrint();
    return 0;
}
<|endoftext|>


<commit_before>// $Id$
/**
 * @file print-RAW-HLTdecision.C
 * @brief Print HLT decisions per event of RAW data
 * The uses the RawReader interface for data access and can be used
 * with different types of data as long as a RawReader is implemented.
 *
 *

 * Usage: aliroot -b -q print-RAW-HLTdecision.C'(input, minEvent, maxEvent)'
 *
 * For help: aliroot -b -q print-RAW-HLTdecision.C
 *

 *
 * The input file can be a file on Grid like e.g.
 * "alien:///alice/data/2009/LHC09d/000104321/raw/09000104321018.30.root"
 * In that case you need a valid token in order to connect to the Grid.
 * Use 'alien-token-init' from your alien installation.
 *
 * @author Matthias.Richter@ift.uib.no
 * @ingroup alihlt_programs
 */
void print_RAW_HLTdecision(const char* rawFileName,
			   int minEvent=0, int maxEvent=-1)
{
  AliHLTLogging log;
  log.SetGlobalLoggingLevel(0x7c);

  TString strfile=rawFileName;
  if (strfile.Contains("://") && !strfile.Contains("local://")) {
    TGrid::Connect("alien");
  }

  AliRawReader* rawReader=NULL;
  if (rawFileName && rawFileName[0]!=0) rawReader=AliRawReader::Create(rawFileName);
  if (!rawFileName || rawFileName[0]==0 || !rawReader) {
    if (rawFileName && rawFileName[0]!=0)
      cerr << "can not open raw reader " << rawFileName << endl;
    else
      cerr << "print-RAW-HLTdecision.C Print HLT decisions per event of RAW data" << endl;
    cerr << "===============================================================" << endl;
    cerr << "usage: aliroot -b -q -l print-RAW-HLTdecision.C'(rawFileName" << endl;
    cerr << "                                                 minEvent, maxEvent)'" << endl << endl;
    cerr << "  Parameter:" << endl;
    cerr << "       rawFileName      e.g \"raw.root\", \"./\"" << endl;
    cerr << "       minEvent         first event (optional)" << endl;
    cerr << "       maxEvent         last event (optional)" << endl;
    cerr << "===============================================================" << endl;
    return;
  }

  rawReader->RewindEvents();
  int event=0;
  if (!rawReader->NextEvent()) {
    cout << "no events found in " << rawFileName << endl;
    return;
  }

  AliHLTOUT* pHLTOUT=AliHLTOUT::New(rawReader);
  if (!pHLTOUT) {
    cerr << "can not create HLTOUT instance" << endl;
    return;
  }

  do {
    if (minEvent>=0 && event<minEvent) continue;
    cout << "=====================  event " << event << "  ==========================" << endl;
    if (pHLTOUT->Init()<0) {
      cerr << "failed to initialize HLTOUT for event " << event << endl;
      break;
    }
    bool found=false;
    do {
      TObject* decision=NULL;
      // in the original implementation, the GlobalTriggerDecision has been
      // sent with data type kAliHLTDataTypeTObject, thats why we check for
      // both
      if (pHLTOUT->SelectFirstDataBlock(kAliHLTDataTypeGlobalTrigger)>=0 ||
	  pHLTOUT->SelectFirstDataBlock(kAliHLTDataTypeTObject)>=0) {
	decision=pHLTOUT->GetDataObject();
      }
      if (decision) {
	if (decision->IsA() == AliHLTGlobalTriggerDecision::Class()) {
	  cout << "HLT Global Trigger: " << decision->GetOption() << "   " << decision->GetTitle() << endl;
	  decision->Print();
	  found=true;
	}
	pHLTOUT->ReleaseDataObject(decision);
      }
    } while (!found && pHLTOUT->SelectNextDataBlock());

    if (!found) {
      cout << "   no HLT decision found" << endl;
    }
    pHLTOUT->Reset();
    event++;
  } while (rawReader->NextEvent() && (maxEvent<0 || event<maxEvent));
}

void print_RAW_HLTdecision()
{
  // print usage
  print_RAW_HLTdecision(NULL);
}
<commit_msg>Fix for infinite loop.<commit_after>// $Id$
/**
 * @file print-RAW-HLTdecision.C
 * @brief Print HLT decisions per event of RAW data
 * The uses the RawReader interface for data access and can be used
 * with different types of data as long as a RawReader is implemented.
 *
 *

 * Usage: aliroot -b -q print-RAW-HLTdecision.C'(input, minEvent, maxEvent)'
 *
 * For help: aliroot -b -q print-RAW-HLTdecision.C
 *

 *
 * The input file can be a file on Grid like e.g.
 * "alien:///alice/data/2009/LHC09d/000104321/raw/09000104321018.30.root"
 * In that case you need a valid token in order to connect to the Grid.
 * Use 'alien-token-init' from your alien installation.
 *
 * @author Matthias.Richter@ift.uib.no
 * @ingroup alihlt_programs
 */
void print_RAW_HLTdecision(const char* rawFileName,
			   int minEvent=0, int maxEvent=-1)
{
  AliHLTLogging log;
  log.SetGlobalLoggingLevel(0x7c);

  TString strfile=rawFileName;
  if (strfile.Contains("://") && !strfile.Contains("local://")) {
    TGrid::Connect("alien");
  }

  AliRawReader* rawReader=NULL;
  if (rawFileName && rawFileName[0]!=0) rawReader=AliRawReader::Create(rawFileName);
  if (!rawFileName || rawFileName[0]==0 || !rawReader) {
    if (rawFileName && rawFileName[0]!=0)
      cerr << "can not open raw reader " << rawFileName << endl;
    else
      cerr << "print-RAW-HLTdecision.C Print HLT decisions per event of RAW data" << endl;
    cerr << "===============================================================" << endl;
    cerr << "usage: aliroot -b -q -l print-RAW-HLTdecision.C'(rawFileName" << endl;
    cerr << "                                                 minEvent, maxEvent)'" << endl << endl;
    cerr << "  Parameter:" << endl;
    cerr << "       rawFileName      e.g \"raw.root\", \"./\"" << endl;
    cerr << "       minEvent         first event (optional)" << endl;
    cerr << "       maxEvent         last event (optional)" << endl;
    cerr << "===============================================================" << endl;
    return;
  }

  rawReader->RewindEvents();
  int event=0;
  if (!rawReader->NextEvent()) {
    cout << "no events found in " << rawFileName << endl;
    return;
  }

  AliHLTOUT* pHLTOUT=AliHLTOUT::New(rawReader);
  if (!pHLTOUT) {
    cerr << "can not create HLTOUT instance" << endl;
    return;
  }

  do {
    if (minEvent>=0 && event<minEvent) continue;
    cout << "=====================  event " << event << "  ==========================" << endl;
    if (pHLTOUT->Init()<0) {
      cerr << "failed to initialize HLTOUT for event " << event << endl;
      break;
    }
    bool found=false;

    // in the original implementation, the GlobalTriggerDecision has been
    // sent with data type kAliHLTDataTypeTObject, thats why we check for
    // both
    if (pHLTOUT->SelectFirstDataBlock(kAliHLTDataTypeGlobalTrigger)>=0 ||
        pHLTOUT->SelectFirstDataBlock(kAliHLTDataTypeTObject)>=0) {
      do {
        TObject* decision = pHLTOUT->GetDataObject();
        if (decision) {
	  if (decision->IsA() == AliHLTGlobalTriggerDecision::Class()) {
	    cout << "HLT Global Trigger: " << decision->GetOption() << "   " << decision->GetTitle() << endl;
	    decision->Print();
	    found=true;
	  }
	  pHLTOUT->ReleaseDataObject(decision);
        }
      } while (!found && pHLTOUT->SelectNextDataBlock());
    }

    if (!found) {
      cout << "   no HLT decision found" << endl;
    }
    pHLTOUT->Reset();
    event++;
  } while (rawReader->NextEvent() && (maxEvent<0 || event<maxEvent));
}

void print_RAW_HLTdecision()
{
  // print usage
  print_RAW_HLTdecision(NULL);
}
<|endoftext|>


<commit_before>// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "printing/units.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "testing/gtest/include/gtest/gtest-spi.h"

using namespace printing;

TEST(UnitsTest, Convertions) {
  EXPECT_EQ(100, ConvertUnit(100, 100, 100));
  EXPECT_EQ(-100, ConvertUnit(-100, 100, 100));
  EXPECT_EQ(0, ConvertUnit(0, 100, 100));
  EXPECT_EQ(99, ConvertUnit(99, 100, 100));
  EXPECT_EQ(101, ConvertUnit(101, 100, 100));
  EXPECT_EQ(99900, ConvertUnit(999, 10, 1000));
  EXPECT_EQ(100100, ConvertUnit(1001, 10, 1000));

  // Rounding.
  EXPECT_EQ(10, ConvertUnit(999, 1000, 10));
  EXPECT_EQ(10, ConvertUnit(950, 1000, 10));
  EXPECT_EQ(9, ConvertUnit(949, 1000, 10));
  EXPECT_EQ(10, ConvertUnit(1001, 1000, 10));
  EXPECT_EQ(10, ConvertUnit(1049, 1000, 10));
  EXPECT_EQ(11, ConvertUnit(1050, 1000, 10));
  EXPECT_EQ(-10, ConvertUnit(-999, 1000, 10));
  EXPECT_EQ(-10, ConvertUnit(-950, 1000, 10));
  EXPECT_EQ(-9, ConvertUnit(-949, 1000, 10));
  EXPECT_EQ(-10, ConvertUnit(-1001, 1000, 10));
  EXPECT_EQ(-10, ConvertUnit(-1049, 1000, 10));
  EXPECT_EQ(-11, ConvertUnit(-1050, 1000, 10));

  EXPECT_EQ(0, ConvertUnit(2, 1000000000, 1));
  EXPECT_EQ(2000000000, ConvertUnit(2, 1, 1000000000));
  EXPECT_EQ(4000000000U,
            static_cast<unsigned int>(ConvertUnit(2, 1, 2000000000)));

  EXPECT_EQ(100, ConvertUnitDouble(100, 100, 100));
  EXPECT_EQ(-100, ConvertUnitDouble(-100, 100, 100));
  EXPECT_EQ(0, ConvertUnitDouble(0, 100, 100));
  EXPECT_DOUBLE_EQ(0.000002, ConvertUnitDouble(2, 1000, 0.001));
  EXPECT_EQ(2000000, ConvertUnitDouble(2, 0.001, 1000));

  EXPECT_EQ(kHundrethsMMPerInch, ConvertMilliInchToHundredThousanthMeter(1000));
  EXPECT_EQ(-kHundrethsMMPerInch,
            ConvertMilliInchToHundredThousanthMeter(-1000));
  EXPECT_EQ(0, ConvertMilliInchToHundredThousanthMeter(0));
  EXPECT_EQ(1000, ConvertHundredThousanthMeterToMilliInch(kHundrethsMMPerInch));
  EXPECT_EQ(-1000,
            ConvertHundredThousanthMeterToMilliInch(-kHundrethsMMPerInch));
  EXPECT_EQ(0, ConvertHundredThousanthMeterToMilliInch(0));

  EXPECT_EQ(8.25, ConvertPixelsToPointDouble(11.0));
  // Round down.
  EXPECT_EQ(8, ConvertPixelsToPoint(11));
  EXPECT_EQ(7.5, ConvertPixelsToPointDouble(10.0));
  // Round up.
  EXPECT_EQ(8, ConvertPixelsToPoint(10));
  EXPECT_EQ(0, ConvertPixelsToPoint(0));
}
<commit_msg>printing: Replace "using namespace printing;" wrapping the tests around "namespace printing { ... }"<commit_after>// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "printing/units.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "testing/gtest/include/gtest/gtest-spi.h"

namespace printing {

TEST(UnitsTest, Convertions) {
  EXPECT_EQ(100, ConvertUnit(100, 100, 100));
  EXPECT_EQ(-100, ConvertUnit(-100, 100, 100));
  EXPECT_EQ(0, ConvertUnit(0, 100, 100));
  EXPECT_EQ(99, ConvertUnit(99, 100, 100));
  EXPECT_EQ(101, ConvertUnit(101, 100, 100));
  EXPECT_EQ(99900, ConvertUnit(999, 10, 1000));
  EXPECT_EQ(100100, ConvertUnit(1001, 10, 1000));

  // Rounding.
  EXPECT_EQ(10, ConvertUnit(999, 1000, 10));
  EXPECT_EQ(10, ConvertUnit(950, 1000, 10));
  EXPECT_EQ(9, ConvertUnit(949, 1000, 10));
  EXPECT_EQ(10, ConvertUnit(1001, 1000, 10));
  EXPECT_EQ(10, ConvertUnit(1049, 1000, 10));
  EXPECT_EQ(11, ConvertUnit(1050, 1000, 10));
  EXPECT_EQ(-10, ConvertUnit(-999, 1000, 10));
  EXPECT_EQ(-10, ConvertUnit(-950, 1000, 10));
  EXPECT_EQ(-9, ConvertUnit(-949, 1000, 10));
  EXPECT_EQ(-10, ConvertUnit(-1001, 1000, 10));
  EXPECT_EQ(-10, ConvertUnit(-1049, 1000, 10));
  EXPECT_EQ(-11, ConvertUnit(-1050, 1000, 10));

  EXPECT_EQ(0, ConvertUnit(2, 1000000000, 1));
  EXPECT_EQ(2000000000, ConvertUnit(2, 1, 1000000000));
  EXPECT_EQ(4000000000U,
            static_cast<unsigned int>(ConvertUnit(2, 1, 2000000000)));

  EXPECT_EQ(100, ConvertUnitDouble(100, 100, 100));
  EXPECT_EQ(-100, ConvertUnitDouble(-100, 100, 100));
  EXPECT_EQ(0, ConvertUnitDouble(0, 100, 100));
  EXPECT_DOUBLE_EQ(0.000002, ConvertUnitDouble(2, 1000, 0.001));
  EXPECT_EQ(2000000, ConvertUnitDouble(2, 0.001, 1000));

  EXPECT_EQ(kHundrethsMMPerInch, ConvertMilliInchToHundredThousanthMeter(1000));
  EXPECT_EQ(-kHundrethsMMPerInch,
            ConvertMilliInchToHundredThousanthMeter(-1000));
  EXPECT_EQ(0, ConvertMilliInchToHundredThousanthMeter(0));
  EXPECT_EQ(1000, ConvertHundredThousanthMeterToMilliInch(kHundrethsMMPerInch));
  EXPECT_EQ(-1000,
            ConvertHundredThousanthMeterToMilliInch(-kHundrethsMMPerInch));
  EXPECT_EQ(0, ConvertHundredThousanthMeterToMilliInch(0));

  EXPECT_EQ(8.25, ConvertPixelsToPointDouble(11.0));
  // Round down.
  EXPECT_EQ(8, ConvertPixelsToPoint(11));
  EXPECT_EQ(7.5, ConvertPixelsToPointDouble(10.0));
  // Round up.
  EXPECT_EQ(8, ConvertPixelsToPoint(10));
  EXPECT_EQ(0, ConvertPixelsToPoint(0));
}

}  // namespace printing
<|endoftext|>


<commit_before>/*******************************************************************************

Licensed to the OpenCOR team under one or more contributor license agreements.
See the NOTICE.txt file distributed with this work for additional information
regarding copyright ownership. The OpenCOR team 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.

*******************************************************************************/

//==============================================================================
// Viewer widget
//==============================================================================

#include "viewerwidget.h"

//==============================================================================

#include <Qt>

//==============================================================================

#include <QAction>
#include <QCursor>
#include <QDomDocument>
#include <QIcon>
#include <QMenu>
#include <QPainter>
#include <QPaintEvent>
#include <QPalette>
#include <QPoint>
#include <QRectF>

//==============================================================================

#include "qwt_mathml_text_engine.h"

//==============================================================================

namespace OpenCOR {
namespace Viewer {

//==============================================================================

ViewerWidget::ViewerWidget(QWidget *pParent) :
    Widget(pParent),
    mMathmlDocument(QwtMathMLDocument()),
    mOneOverMathmlDocumentWidth(0),
    mOneOverMathmlDocumentHeight(0),
    mContents(QString()),
    mError(false)
{
    // Create our context menu

    mContextMenu = new QMenu(this);

    mOptimiseFontSizeAction = newAction(this);
    mDigitGroupingAction = newAction(this);

    connect(mOptimiseFontSizeAction, SIGNAL(triggered()),
            this, SLOT(update()));
    connect(mOptimiseFontSizeAction, SIGNAL(toggled(bool)),
            this, SIGNAL(optimiseFontSizeChanged(const bool &)));

    connect(mDigitGroupingAction, SIGNAL(triggered()),
            this, SLOT(updateContents()));
    connect(mDigitGroupingAction, SIGNAL(toggled(bool)),
            this, SIGNAL(digitGroupingChanged(const bool &)));

    mContextMenu->addAction(mOptimiseFontSizeAction);
    mContextMenu->addSeparator();
    mContextMenu->addAction(mDigitGroupingAction);

    // We want a context menu

    setContextMenuPolicy(Qt::CustomContextMenu);

    connect(this, SIGNAL(customContextMenuRequested(const QPoint &)),
            this, SLOT(showCustomContextMenu(const QPoint &)));

    // Retranslate ourselves, so that our action is properly set

    retranslateUi();
}

//==============================================================================

void ViewerWidget::retranslateUi()
{
    // Retranslate our actions

    mOptimiseFontSizeAction->setText(tr("Optimise Font Size"));
    mDigitGroupingAction->setText(tr("Digit Grouping"));
}

//==============================================================================

QString ViewerWidget::contents() const
{
    // Return our contents

    return mContents;
}

//==============================================================================

void ViewerWidget::setContents(const QString &pContents)
{
    // Set our contents

    if (!pContents.compare(mContents))
        return;

    // Try to set our contents to our MathML document

    if (mMathmlDocument.setContent(pContents)) {
        mContents = pContents;
        mError = false;

        // Process and reset our contents, shoudl we want to do digit grouping

        if (digitGrouping())
            mMathmlDocument.setContent(processedContents());

        // Determine (the inverse of) the size of our contents when rendered
        // using a font size of 100 points
        // Note: when setting the contents, QwtMathMLDocument recomputes its
        //       layout. Now, because we want the contents to be rendered as
        //       optimally as possible, we use a big font size, so that when we
        //       actually need to render the contents (see paintEvent()), we can
        //       do so optimally...

        mMathmlDocument.setBaseFontPointSize(100);

        QSizeF mathmlDocumentSize = mMathmlDocument.size();

        mOneOverMathmlDocumentWidth  = 1.0/mathmlDocumentSize.width();
        mOneOverMathmlDocumentHeight = 1.0/mathmlDocumentSize.height();
    } else {
        mContents = QString();
        mError = pContents.size();
    }

    // Update ourselves

    update();
}

//==============================================================================

bool ViewerWidget::error() const
{
    // Return whether there is an error

    return mError;
}

//==============================================================================

void ViewerWidget::setError(const bool &pError)
{
    // Keep track of whether there is an error

    if (pError == mError)
        return;

    mContents = QString();
    mError = pError;

    // Update ourselves

    update();
}

//==============================================================================

bool ViewerWidget::optimiseFontSize() const
{
    // Return whether we optimise our font size

    return mOptimiseFontSizeAction->isChecked();
}

//==============================================================================

void ViewerWidget::setOptimiseFontSize(const bool &pOptimiseFontSize)
{
    // Keep track of whether we should optimise our font size

    if (pOptimiseFontSize == optimiseFontSize())
        return;

    mOptimiseFontSizeAction->setChecked(pOptimiseFontSize);

    // Let people know about the new value

    emit optimiseFontSizeChanged(pOptimiseFontSize);

    // Update ourselves

    update();
}

//==============================================================================

bool ViewerWidget::digitGrouping() const
{
    // Return whether we do digit grouping

    return mDigitGroupingAction->isChecked();
}

//==============================================================================

void ViewerWidget::setDigitGrouping(const bool &pDigitGrouping)
{
    // Keep track of whether we do digit grouping

    if (pDigitGrouping == digitGrouping())
        return;

    mDigitGroupingAction->setChecked(pDigitGrouping);

    // Let people know about the new value

    emit digitGroupingChanged(pDigitGrouping);

    // Update ourselves

    update();
}

//==============================================================================

void ViewerWidget::paintEvent(QPaintEvent *pEvent)
{
    // Make sure that we have a width and height

    QRectF rect = pEvent->rect();

    if (!rect.width() || !rect.height())
        return;

    // Clear our background

    QPainter painter(this);
    QColor backgroundColor = QColor(palette().color(QPalette::Base));

    painter.fillRect(rect, backgroundColor);

    // Render our contents or show a warning sign, depending on whether our
    // contents is valid

    if (mError) {
        // We want to show that there is an error, so render a stretched warning
        // icon in our center

        QIcon icon = QIcon(":Viewer_warning");
        QSize iconSize = icon.availableSizes().first();

        int painterRectWidth = iconSize.width();
        int painterRectHeight = iconSize.height();

        if (rect.width() < rect.height())
            painterRectHeight *= rect.height()/rect.width();
        else
            painterRectWidth *= rect.width()/rect.height();

        if (painterRectHeight % 2)
            ++painterRectHeight;

        if (painterRectWidth % 2)
            ++painterRectWidth;

        QRect painterRect = QRect(0, 0, painterRectWidth, painterRectHeight);
        // Note: both painterRectWidth and painterRectHeight have even values.
        //       This ensures that when we get resized, then the warning icon
        //       remains nicely centered (while it would otherwise shifts to the
        //       left/right or top/bottom, depending on our width or height,
        //       respectively...

        painter.setWindow(painterRect);

        icon.paint(&painter, painterRect);
    } else {
        // Customise our MathML document
        // Note: to go for 100% of the 'optimal' font size might result in the
        //       edges of the contents being clipped on Windows (compared to
        //       Linux and OS X) or in some cases on Linux and OS X (e.g. if the
        //       contents includes a square root), hence we go for 75% of the
        //       'optimal' font size instead...

        mMathmlDocument.setBackgroundColor(backgroundColor);
        mMathmlDocument.setForegroundColor(QColor(palette().color(QPalette::Text)));

        mMathmlDocument.setBaseFontPointSize(mOptimiseFontSizeAction->isChecked()?
                                                 qRound(75.0*qMin(mOneOverMathmlDocumentWidth*width(),
                                                                  mOneOverMathmlDocumentHeight*height())):
                                                 font().pointSize());

        // Render our contents

        QSizeF mathmlDocumentSize = mMathmlDocument.size();

        mMathmlDocument.paint(&painter, QPointF(0.5*(rect.width()-mathmlDocumentSize.width()),
                                                0.5*(rect.height()-mathmlDocumentSize.height())));
    }

    // Accept the event

    pEvent->accept();
}

//==============================================================================

QSize ViewerWidget::minimumSizeHint() const
{
    // Suggest a default minimum size for our viewer widget

    return defaultSize(0.03);
}

//==============================================================================

QSize ViewerWidget::sizeHint() const
{
    // Suggest a default size for our viewer widget
    // Note: this is critical if we want a docked widget, with a viewer widget
    //       on it, to have a decent size when docked to the main window...

    return defaultSize(0.1);
}

//==============================================================================

QAction * ViewerWidget::newAction(QObject *pParent)
{
    // Create and return a checkable and checked action

    QAction *res = new QAction(pParent);

    res->setCheckable(true);
    res->setChecked(true);

    return res;
}

//==============================================================================

void ViewerWidget::processNode(const QDomNode &pDomNode) const
{
    // Go through the node's children and process them

    for (int i = 0, iMax = pDomNode.childNodes().count(); i < iMax; ++i) {
        QDomNode domNode = pDomNode.childNodes().at(i);

        // Check whether we want to do digit grouping and whether the node's
        // parent is an mn element

        if (digitGrouping() && !domNode.parentNode().nodeName().compare("mn")) {
            // We want to do digit grouping and whether the node's parent is an
            // mn element, so check whether the value of that element is a valid
            // number

            bool domNodeValueValid;
            QString domNodeValue = domNode.nodeValue();

            domNodeValue.toDouble(&domNodeValueValid);

            if (domNodeValueValid) {
                // The number is valid, so do digit grouping on it

                int decimalPointPos = domNodeValue.indexOf(".");

                if (decimalPointPos != -1) {
                    QString beforeDecimalPoint = domNodeValue.left(decimalPointPos);
                    domNodeValue = domNodeValue.right(domNodeValue.length()-decimalPointPos);
                    bool maybeDigit = true;
                    int nbOfDigits = -1;

                    for (int i = beforeDecimalPoint.length()-1; i >= 0; --i) {
                        if (maybeDigit && beforeDecimalPoint[i].isDigit()) {
                            if (++nbOfDigits == 3) {
                                domNodeValue = ","+domNodeValue;

                                nbOfDigits = 0;
                            }
                        } else {
                            maybeDigit = false;
                        }

                        domNodeValue = beforeDecimalPoint[i]+domNodeValue;
                    }

                    domNode.setNodeValue(domNodeValue);
                }
            }
        }

        processNode(domNode);
    }
}

//==============================================================================

QString ViewerWidget::processedContents() const
{
    // Process and return our processed contents

    QDomDocument domDocument;

    if (domDocument.setContent(mContents)) {
        QDomNode domNode = domDocument.documentElement();

        processNode(domNode);

        return domDocument.toString(-1);
    } else {
        // We should never reach this point (since we should only come here if
        // our contents is valid), but better be safe than sorry...

        return QString();
    }
}

//==============================================================================

void ViewerWidget::showCustomContextMenu(const QPoint &pPosition) const
{
    Q_UNUSED(pPosition);

    // Show our custom context menu

    mContextMenu->exec(QCursor::pos());
}

//==============================================================================

void ViewerWidget::updateContents()
{
    // Update our contents and therefore ourselves (through setContents())

    QString contents = mContents;

    mContents = QString();

    setContents(contents);
}

//==============================================================================

}   // namespace Viewer
}   // namespace OpenCOR

//==============================================================================
// End of file
//==============================================================================
<commit_msg>Some minor cleaning up.<commit_after>/*******************************************************************************

Licensed to the OpenCOR team under one or more contributor license agreements.
See the NOTICE.txt file distributed with this work for additional information
regarding copyright ownership. The OpenCOR team 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.

*******************************************************************************/

//==============================================================================
// Viewer widget
//==============================================================================

#include "viewerwidget.h"

//==============================================================================

#include <Qt>

//==============================================================================

#include <QAction>
#include <QCursor>
#include <QDomDocument>
#include <QIcon>
#include <QMenu>
#include <QPainter>
#include <QPaintEvent>
#include <QPalette>
#include <QPoint>
#include <QRectF>

//==============================================================================

#include "qwt_mathml_text_engine.h"

//==============================================================================

namespace OpenCOR {
namespace Viewer {

//==============================================================================

ViewerWidget::ViewerWidget(QWidget *pParent) :
    Widget(pParent),
    mMathmlDocument(QwtMathMLDocument()),
    mOneOverMathmlDocumentWidth(0),
    mOneOverMathmlDocumentHeight(0),
    mContents(QString()),
    mError(false)
{
    // Create our context menu

    mContextMenu = new QMenu(this);

    mOptimiseFontSizeAction = newAction(this);
    mDigitGroupingAction = newAction(this);

    connect(mOptimiseFontSizeAction, SIGNAL(triggered()),
            this, SLOT(update()));
    connect(mOptimiseFontSizeAction, SIGNAL(toggled(bool)),
            this, SIGNAL(optimiseFontSizeChanged(const bool &)));

    connect(mDigitGroupingAction, SIGNAL(triggered()),
            this, SLOT(updateContents()));
    connect(mDigitGroupingAction, SIGNAL(toggled(bool)),
            this, SIGNAL(digitGroupingChanged(const bool &)));

    mContextMenu->addAction(mOptimiseFontSizeAction);
    mContextMenu->addSeparator();
    mContextMenu->addAction(mDigitGroupingAction);

    // We want a context menu

    setContextMenuPolicy(Qt::CustomContextMenu);

    connect(this, SIGNAL(customContextMenuRequested(const QPoint &)),
            this, SLOT(showCustomContextMenu(const QPoint &)));

    // Retranslate ourselves, so that our action is properly set

    retranslateUi();
}

//==============================================================================

void ViewerWidget::retranslateUi()
{
    // Retranslate our actions

    mOptimiseFontSizeAction->setText(tr("Optimise Font Size"));
    mDigitGroupingAction->setText(tr("Digit Grouping"));
}

//==============================================================================

QString ViewerWidget::contents() const
{
    // Return our contents

    return mContents;
}

//==============================================================================

void ViewerWidget::setContents(const QString &pContents)
{
    // Set our contents

    if (!pContents.compare(mContents))
        return;

    // Try to set our contents to our MathML document

    mContents = pContents;

    if (mMathmlDocument.setContent(pContents)) {
        mError = false;

        // Process and reset our contents, shoudl we want to do digit grouping

        if (digitGrouping())
            mMathmlDocument.setContent(processedContents());

        // Determine (the inverse of) the size of our contents when rendered
        // using a font size of 100 points
        // Note: when setting the contents, QwtMathMLDocument recomputes its
        //       layout. Now, because we want the contents to be rendered as
        //       optimally as possible, we use a big font size, so that when we
        //       actually need to render the contents (see paintEvent()), we can
        //       do so optimally...

        mMathmlDocument.setBaseFontPointSize(100);

        QSizeF mathmlDocumentSize = mMathmlDocument.size();

        mOneOverMathmlDocumentWidth  = 1.0/mathmlDocumentSize.width();
        mOneOverMathmlDocumentHeight = 1.0/mathmlDocumentSize.height();
    } else {
        mError = pContents.size();
    }

    // Update ourselves

    update();
}

//==============================================================================

bool ViewerWidget::error() const
{
    // Return whether there is an error

    return mError;
}

//==============================================================================

void ViewerWidget::setError(const bool &pError)
{
    // Keep track of whether there is an error

    if (pError == mError)
        return;

    mContents = QString();
    mError = pError;

    // Update ourselves

    update();
}

//==============================================================================

bool ViewerWidget::optimiseFontSize() const
{
    // Return whether we optimise our font size

    return mOptimiseFontSizeAction->isChecked();
}

//==============================================================================

void ViewerWidget::setOptimiseFontSize(const bool &pOptimiseFontSize)
{
    // Keep track of whether we should optimise our font size

    if (pOptimiseFontSize == optimiseFontSize())
        return;

    mOptimiseFontSizeAction->setChecked(pOptimiseFontSize);

    // Let people know about the new value

    emit optimiseFontSizeChanged(pOptimiseFontSize);

    // Update ourselves

    update();
}

//==============================================================================

bool ViewerWidget::digitGrouping() const
{
    // Return whether we do digit grouping

    return mDigitGroupingAction->isChecked();
}

//==============================================================================

void ViewerWidget::setDigitGrouping(const bool &pDigitGrouping)
{
    // Keep track of whether we do digit grouping

    if (pDigitGrouping == digitGrouping())
        return;

    mDigitGroupingAction->setChecked(pDigitGrouping);

    // Let people know about the new value

    emit digitGroupingChanged(pDigitGrouping);

    // Update ourselves

    update();
}

//==============================================================================

void ViewerWidget::paintEvent(QPaintEvent *pEvent)
{
    // Make sure that we have a width and height

    QRectF rect = pEvent->rect();

    if (!rect.width() || !rect.height())
        return;

    // Clear our background

    QPainter painter(this);
    QColor backgroundColor = QColor(palette().color(QPalette::Base));

    painter.fillRect(rect, backgroundColor);

    // Render our contents or show a warning sign, depending on whether our
    // contents is valid

    if (mError) {
        // We want to show that there is an error, so render a stretched warning
        // icon in our center

        QIcon icon = QIcon(":Viewer_warning");
        QSize iconSize = icon.availableSizes().first();

        int painterRectWidth = iconSize.width();
        int painterRectHeight = iconSize.height();

        if (rect.width() < rect.height())
            painterRectHeight *= rect.height()/rect.width();
        else
            painterRectWidth *= rect.width()/rect.height();

        if (painterRectHeight % 2)
            ++painterRectHeight;

        if (painterRectWidth % 2)
            ++painterRectWidth;

        QRect painterRect = QRect(0, 0, painterRectWidth, painterRectHeight);
        // Note: both painterRectWidth and painterRectHeight have even values.
        //       This ensures that when we get resized, then the warning icon
        //       remains nicely centered (while it would otherwise shifts to the
        //       left/right or top/bottom, depending on our width or height,
        //       respectively...

        painter.setWindow(painterRect);

        icon.paint(&painter, painterRect);
    } else {
        // Customise our MathML document
        // Note: to go for 100% of the 'optimal' font size might result in the
        //       edges of the contents being clipped on Windows (compared to
        //       Linux and OS X) or in some cases on Linux and OS X (e.g. if the
        //       contents includes a square root), hence we go for 75% of the
        //       'optimal' font size instead...

        mMathmlDocument.setBackgroundColor(backgroundColor);
        mMathmlDocument.setForegroundColor(QColor(palette().color(QPalette::Text)));

        mMathmlDocument.setBaseFontPointSize(mOptimiseFontSizeAction->isChecked()?
                                                 qRound(75.0*qMin(mOneOverMathmlDocumentWidth*width(),
                                                                  mOneOverMathmlDocumentHeight*height())):
                                                 font().pointSize());

        // Render our contents

        QSizeF mathmlDocumentSize = mMathmlDocument.size();

        mMathmlDocument.paint(&painter, QPointF(0.5*(rect.width()-mathmlDocumentSize.width()),
                                                0.5*(rect.height()-mathmlDocumentSize.height())));
    }

    // Accept the event

    pEvent->accept();
}

//==============================================================================

QSize ViewerWidget::minimumSizeHint() const
{
    // Suggest a default minimum size for our viewer widget

    return defaultSize(0.03);
}

//==============================================================================

QSize ViewerWidget::sizeHint() const
{
    // Suggest a default size for our viewer widget
    // Note: this is critical if we want a docked widget, with a viewer widget
    //       on it, to have a decent size when docked to the main window...

    return defaultSize(0.1);
}

//==============================================================================

QAction * ViewerWidget::newAction(QObject *pParent)
{
    // Create and return a checkable and checked action

    QAction *res = new QAction(pParent);

    res->setCheckable(true);
    res->setChecked(true);

    return res;
}

//==============================================================================

void ViewerWidget::processNode(const QDomNode &pDomNode) const
{
    // Go through the node's children and process them

    for (int i = 0, iMax = pDomNode.childNodes().count(); i < iMax; ++i) {
        QDomNode domNode = pDomNode.childNodes().at(i);

        // Check whether we want to do digit grouping and whether the node's
        // parent is an mn element

        if (digitGrouping() && !domNode.parentNode().nodeName().compare("mn")) {
            // We want to do digit grouping and whether the node's parent is an
            // mn element, so check whether the value of that element is a valid
            // number

            bool domNodeValueValid;
            QString domNodeValue = domNode.nodeValue();

            domNodeValue.toDouble(&domNodeValueValid);

            if (domNodeValueValid) {
                // The number is valid, so do digit grouping on it

                int decimalPointPos = domNodeValue.indexOf(".");

                if (decimalPointPos != -1) {
                    QString beforeDecimalPoint = domNodeValue.left(decimalPointPos);
                    domNodeValue = domNodeValue.right(domNodeValue.length()-decimalPointPos);
                    bool maybeDigit = true;
                    int nbOfDigits = -1;

                    for (int i = beforeDecimalPoint.length()-1; i >= 0; --i) {
                        if (maybeDigit && beforeDecimalPoint[i].isDigit()) {
                            if (++nbOfDigits == 3) {
                                domNodeValue = ","+domNodeValue;

                                nbOfDigits = 0;
                            }
                        } else {
                            maybeDigit = false;
                        }

                        domNodeValue = beforeDecimalPoint[i]+domNodeValue;
                    }

                    domNode.setNodeValue(domNodeValue);
                }
            }
        }

        processNode(domNode);
    }
}

//==============================================================================

QString ViewerWidget::processedContents() const
{
    // Process and return our processed contents

    if (mContents.isEmpty())
        return QString();

    QDomDocument domDocument;

    if (domDocument.setContent(mContents)) {
        QDomNode domNode = domDocument.documentElement();

        processNode(domNode);

        return domDocument.toString(-1);
    } else {
        // We should never reach this point (since we should only come here if
        // our contents is valid), but better be safe than sorry...

        return QString();
    }
}

//==============================================================================

void ViewerWidget::showCustomContextMenu(const QPoint &pPosition) const
{
    Q_UNUSED(pPosition);

    // Show our custom context menu

    mContextMenu->exec(QCursor::pos());
}

//==============================================================================

void ViewerWidget::updateContents()
{
    // Update our contents and therefore ourselves (through setContents())

    QString contents = mContents;

    mContents = QString();

    setContents(contents);
}

//==============================================================================

}   // namespace Viewer
}   // namespace OpenCOR

//==============================================================================
// End of file
//==============================================================================
<|endoftext|>


<commit_before>// Copyright 2010-2014 RethinkDB, all rights reserved.
#ifndef RDB_PROTOCOL_ARTIFICIAL_TABLE_BACKEND_HPP_
#define RDB_PROTOCOL_ARTIFICIAL_TABLE_BACKEND_HPP_

#include <string>
#include <vector>

#include "rdb_protocol/datum.hpp"

/* `artificial_table_backend_t` is the interface that `artificial_table_t` uses to access
the actual data or configuration. There is one subclass for each table like
`rethinkdb.table_config`, `rethinkdb.table_status`, and so on. */

class artificial_table_backend_t : public home_thread_mixin_t {
public:
    /* Notes:
     1. If `set_row()` is called concurrently with `get_row()` or
        `get_all_primary_keys()`, it is undefined whether the read will see the write or
        not.
     2. `get_primary_key_name()`, `read_all_primary_keys()`, `read_row()` and
        `write_row()` can be called on any thread.
     3. `get_publisher()` can only be called on the home thread. The return value will
        always deliver notifications on that same thread.
    */

    /* Returns the name of the primary key for the table. The return value must not
    change. This must not block. */
    virtual std::string get_primary_key_name() = 0;

    /* Returns the primary keys of all of the rows that exist. */
    virtual bool read_all_primary_keys(
        signal_t *interruptor,
        std::vector<ql::datum_t> *keys_out,
        std::string *error_out) = 0;

    /* Returns the current value of the row, or an empty `counted_t` if no such row
    exists. */
    virtual bool read_row(
        ql::datum_t primary_key,
        signal_t *interruptor,
        ql::datum_t *row_out,
        std::string *error_out) = 0;

    /* Called when the user issues a write command on the row. Calling `set_row()` on a
    row that doesn't exist means an insertion; calling `set_row` with `new_value` an
    empty `counted_t` means a deletion. */
    virtual bool write_row(
        ql::datum_t primary_key,
        ql::datum_t new_value,
        signal_t *interruptor,
        std::string *error_out) = 0;

    /* RSI(reql_admin): Support change feeds. */

protected:
    virtual ~artificial_table_backend_t() { }
};

#endif /* RDB_PROTOCOL_ARTIFICIAL_TABLE_BACKEND_HPP_ */

<commit_msg>Update comments in backend.hpp.<commit_after>// Copyright 2010-2014 RethinkDB, all rights reserved.
#ifndef RDB_PROTOCOL_ARTIFICIAL_TABLE_BACKEND_HPP_
#define RDB_PROTOCOL_ARTIFICIAL_TABLE_BACKEND_HPP_

#include <string>
#include <vector>

#include "rdb_protocol/datum.hpp"

/* `artificial_table_backend_t` is the interface that `artificial_table_t` uses to access
the actual data or configuration. There is one subclass for each table like
`rethinkdb.table_config`, `rethinkdb.table_status`, and so on. */

class artificial_table_backend_t : public home_thread_mixin_t {
public:
    /* Notes:
     1. `read_all_primary_keys()`, `read_row()`, and `write_row()` all return `false` and
        set `*error_out` if an error occurs. Note that if a row is absent in
        `read_row()`, this doesn't count as an error.
     2. If `write_row()` is called concurrently with `read_row()` or
        `read_all_primary_keys()`, it is undefined whether the read will see the write or
        not.
     3. `get_primary_key_name()`, `read_all_primary_keys()`, `read_row()` and
        `write_row()` can be called on any thread. */

    /* Returns the name of the primary key for the table. The return value must not
    change. This must not block. */
    virtual std::string get_primary_key_name() = 0;

    /* Returns the primary keys of all of the rows that exist. */
    virtual bool read_all_primary_keys(
        signal_t *interruptor,
        std::vector<ql::datum_t> *keys_out,
        std::string *error_out) = 0;

    /* Returns the current value of the row, or an empty `datum_t` if no such row
    exists. */
    virtual bool read_row(
        ql::datum_t primary_key,
        signal_t *interruptor,
        ql::datum_t *row_out,
        std::string *error_out) = 0;

    /* Called when the user issues a write command on the row. Calling `write_row()` on a
    row that doesn't exist means an insertion; calling `write_row` with `new_value` an
    empty `datum_t` means a deletion. */
    virtual bool write_row(
        ql::datum_t primary_key,
        ql::datum_t new_value,
        signal_t *interruptor,
        std::string *error_out) = 0;

    /* RSI(reql_admin): Support change feeds. */

protected:
    virtual ~artificial_table_backend_t() { }
};

#endif /* RDB_PROTOCOL_ARTIFICIAL_TABLE_BACKEND_HPP_ */

<|endoftext|>