<commit_before>#include <iostream>
#include <string.h>
#include <boost/tokenizer.hpp>
#include <vector>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <algorithm>
#include <ctype.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <time.h>
#include <errno.h>

using namespace std;
using namespace boost;

class Connector
{
    public:
        Connector();
        ~Connector();
        bool run;
        int type;
        bool runNext();
        bool precedence;

};

Connector::Connector()
{
    // the semicolon connector allows the next command to be execute always
    run = true;
    type = 0; // semicolon connector set to 0
    precedence = false;
}

Connector::~Connector()
{
}
bool Connector::runNext()
{
    if (type == 1) // && connector
    {
        if (!run) // if the first command doesn't succeed  it wont run the next command
        {
            return false;
        }
    }
    else if (type == 2) // || connector
    {
        if (run) // if the first command succeed it wont run the next command
        {
            return false;
        }
    }
    return true;
}
void parseinator(vector<string> input, string& command, string& argument, int& position, Connector& connect, string& flag);
void commandifier(string command, string argument, Connector& connect, string flag);
void test_function(string command, string & flag, string argument, Connector& connect);
int main()
{
    string flag;
    string str;
    string command;
    string argument;
    Connector connect;
    char hostname[100];
    int position = 0;
    while (true)
    {
        printf("%s",getlogin()); // returns string containing name of user logged in
        gethostname(hostname, sizeof hostname); // returns the standard host name for the current machine
        cout << "@" << hostname << "$ ";
        getline(cin, str);
        if (str == "exit") // special command of exit
        {
            break;
        }
        vector<string> instruction; // vector of strings
        typedef tokenizer<boost::char_separator<char> > Tok;
        char_separator<char> sep; // default constructed
        Tok tok(str, sep);;
        for (Tok::iterator tok_iter = tok.begin(); tok_iter != tok.end(); ++tok_iter) // parses the input into a command and argument
        {
	    instruction.push_back(*tok_iter);

	}
	if (instruction[0] != "#") // checks for user input except for comments (#)
        {
            parseinator(instruction, command, argument, position, connect, flag); // parses the input into a command and argument
            commandifier(command, argument, connect, flag); // runs a command with a given argument
            command = "";
            argument = "";
            int vector_size = instruction.size();
            for (; position < vector_size;) // run until the end of the instruction
            {
                parseinator(instruction, command, argument, position, connect, flag);
                if (connect.runNext() && command != "") // checks connector to see if the next command should be ran
	        {
                    commandifier(command, argument, connect, flag);
                }
	        else
	        {
	            while(connect.precedence && position < vector_size)
		        {
		            if(instruction[position] == ")")
		            {
		                connect.precedence = false;
		            }
		            position++;
		        }
	            connect.run = true;
	            command = "";
	        }
	        command = "";
                argument = "";
            }
            position = 0;
        }
    }
    return 0;
}
void parseinator(vector<string> input, string& command, string& argument, int& position, Connector& connect, string& flag)
{
    if (input[position] == "&") // check if string is equal to & connector
    {
        connect.type = 1; // set & connector to 1
        position += 2; // +=2 in order to take && connect
    }
    if (input[position] == "|")
    {
        connect.type = 2; // set | connector to 2
        position += 2; // +=2 in order to take || connector
    }
    if (input[position] == ";")
    {
        connect.type = 0; // set ; connector to 0
        position ++;
    }
    if (input[position] == "(")
    {
        connect.precedence = true;
        position++;
    }
    if (input[position] != "#")
    {
        command = input[position];
        position++;
    }
    if (command == "test")
    {
        if(input[position] == "-")
        {
            position++;
	    if (input[position] == "f")
            {
		flag = "-f";
		position++;
	    }
	    else if (input[position] == "d")
	    {
		flag = "-d";
		position++;
	    }
	    else // DEFAULT E
	    {
		flag = "-e";
		position++;
            }
        }
    }

    int input_size = input.size();
    for (; position < input_size; position++)
    {
        if (input[position] == ")")
        {
            connect.precedence = false;
            position++;
            break;
        }
        if(command == "echo" && input[position] == "\"")
        {
            position++;
            while(input[position] != "\"" && position <= input_size)
            {

                argument += input[position];
                if(!isalnum(input[position].at(0)) && !isalnum(input[position+1].at(0)))
                {

                }
                else
		{
                    argument += " ";
                }
		position++;
            }
            position++;
            break;
        }
        if (input[position] == "#")
        {
            position = input.size();
            break;
        }
        if (input[position] == "&" || input[position] == "|" || input[position] == ";")
	    {
            break;
    	}

        argument += input[position];
        int input_size1 = input.size();
        if (position+1 != input_size1 && command == "echo")
	{
	    //adds spaces when echoing
            argument += " ";
	}
    }
}
void commandifier(string command, string argument, Connector& connect, string flag)
{
    const char * const_command = command.c_str();
    char * char_command = new char[command.size()];
    const char * const_argument = argument.c_str();
    char * char_argument = new char[argument.size()];
    strcpy (char_command, const_command);
    strcpy (char_argument, const_argument);
    char * args[2]; // char pointer array that holds command, and NULL
    char * args1[3]; // char pointer array that holds command, argument, and NULL
    bool no_arg = true;
    bool failed = false;

    if(command == "test" || command == "[")
    {
	test_function(command,flag,argument,connect);
	return;
    }
    // exit command
    if(command == "exit" || argument == "exit")
    {
	exit(1);
    }

    if (argument.size() == 0) // no arguments
    {
        args[0] = char_command; // args will contain the command
        args[1] = NULL;
    }
    else
    {
	no_arg = false; // sets bool no_arg to false
	args1[0] = char_command; // args1 contains command and argument
        args1[1] = char_argument;
        args1[2] = NULL;
    }
    pid_t c_pid, pid; // data type to reporesent process ID's
    int status;
    c_pid = fork(); // create our fork

    if (c_pid < 0) // indicates the fork has fail if its less than 0
    {
        perror("fork failed");
        exit(1);
    }
    else if (c_pid == 0) // in child process
    {
 	if (no_arg) // no argument
	{
        	execvp( args[0], args); // execvp the char pointer array that holds the command only
        	if (execvp(args[0], args) == -1) // if it returns -1 it failed
                {
                    perror("execvp failed");
                    connect.run = false;
                    failed = true;
                }
		exit(3);
	}
	else // with command and argument
	{
		execvp(args1[0], args1); // execvp the char pointer array that holds the command, and argument
		if (execvp(args1[0], args1) == -1) // if it returns -1 it failed
                {
                    perror("execvp failed");
                    connect.run = false;
                    failed = true;
                }
		exit(3);
	}
    }
    else if (c_pid > 0) // parent process
    {
        if((pid = wait(&status)) < 0 )
        {
            perror("wait");
	    exit(3);
        }
        if (WIFEXITED(status))  //Evaluates to a non-zero value if status was returned for a child process that terminated normally.

        {
            if (WEXITSTATUS(status) != 0)
            {
                connect.run = false;
            }
	    else
	    {
		connect.run = true;
            }
        }
       else
        {
            connect.run = true;
        }
     }
}
void test_function(string command, string & flag, string argument, Connector& connect)
{
   const char * arg = argument.c_str();
   struct stat sb; // struct for the stat function
   if(flag == "-f")
   {
        if(stat(arg,&sb) == 0)
        {
            connect.run = true;
	    if(S_ISREG(sb.st_mode))
	    {
 		connect.run = true;
	    }
	    else
	    {
		connect.run = false;
	    }
        }
        else
        {
            connect.run = false;
        }
   }
   else if(flag == "-d")
   {
        if(stat(arg,&sb) == 0)
        {
            connect.run = true;
            if(S_ISDIR(sb.st_mode))
            {
            	connect.run = true;
            }
            else
       	    {
             	connect.run = false;
            }
        }
        else
        {
            connect.run = false;
        }

   }
   else
   {
	if(stat(arg,&sb) == 0)
	{
	    connect.run = true;
 	}
	else
	{
	    connect.run = false;
	}
   }
}
<commit_msg>Update rshell.cpp<commit_after>#include <iostream>
#include <string.h>
#include <boost/tokenizer.hpp>
#include <vector>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <algorithm>
#include <ctype.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <time.h>
#include <errno.h>

using namespace std;
using namespace boost;

class Connector
{
    public:
        Connector();
        ~Connector();
        bool run;
        int type;
        bool runNext();
        bool precedence;

};

Connector::Connector()
{
    // the semicolon connector allows the next command to be execute always
    run = true;
    type = 0; // semicolon connector set to 0
    precedence = false;
}

Connector::~Connector()
{
}
bool Connector::runNext()
{
    if (type == 1) // && connector
    {
        if (!run) // if the first command doesn't succeed  it wont run the next command
        {
            return false;
        }
    }
    else if (type == 2) // || connector
    {
        if (run) // if the first command succeed it wont run the next command
        {
            return false;
        }
    }
    return true;
}
void parseinator(vector<string> input, string& command, string& argument, int& position, Connector& connect, string& flag);
void commandifier(string command, string argument, Connector& connect, string flag);
void test_function(string command, string & flag, string argument, Connector& connect);
int main()
{
    string flag;
    string str;
    string command;
    string argument;
    Connector connect;
    char hostname[100];
    int position = 0;
    while (true)
    {
        printf("%s",getlogin()); // returns string containing name of user logged in
        gethostname(hostname, sizeof hostname); // returns the standard host name for the current machine
        cout << "@" << hostname << "$ ";
        getline(cin, str);
        if (str == "exit") // special command of exit
        {
            break;
        }
        vector<string> instruction; // vector of strings
        typedef tokenizer<boost::char_separator<char> > Tok;
        char_separator<char> sep; // default constructed
        Tok tok(str, sep);;
        for (Tok::iterator tok_iter = tok.begin(); tok_iter != tok.end(); ++tok_iter) // parses the input into a command and argument
        {
	    instruction.push_back(*tok_iter);

	}
	if (instruction[0] != "#") // checks for user input except for comments (#)
        {
            parseinator(instruction, command, argument, position, connect, flag); // parses the input into a command and argument
            commandifier(command, argument, connect, flag); // runs a command with a given argument
            command = "";
            argument = "";
            int vector_size = instruction.size();
            for (; position < vector_size;) // run until the end of the instruction
            {
                parseinator(instruction, command, argument, position, connect, flag);
                if (connect.runNext() && command != "") // checks connector to see if the next command should be ran
	        {
                    commandifier(command, argument, connect, flag);
                }
	        else
	        {
	            while(connect.precedence && position < vector_size)
		        {
		            if(instruction[position] == ")")
		            {
		                connect.precedence = false;
		            }
		            position++;
		        }
	            connect.run = true;
	            command = "";
	        }
	        command = "";
                argument = "";
            }
            position = 0;
        }
    }
    return 0;
}
void parseinator(vector<string> input, string& command, string& argument, int& position, Connector& connect, string& flag)
{
    if (input[position] == "&") // check if string is equal to & connector
    {
        connect.type = 1; // set & connector to 1
        position += 2; // +=2 in order to take && connect
    }
    if (input[position] == "|")
    {
        connect.type = 2; // set | connector to 2
        position += 2; // +=2 in order to take || connector
    }
    if (input[position] == ";")
    {
        connect.type = 0; // set ; connector to 0
        position ++;
    }
    if (input[position] == "(")
    {
        connect.precedence = true;
        position++;
    }
    if (input[position] != "#")
    {
        command = input[position];
        position++;
    }
    if (command == "test" || command == "[")
    {
        if(input[position] == "-")
        {
            position++;
	    if (input[position] == "f")
            {
		flag = "-f";
		position++;
	    }
	    else if (input[position] == "d")
	    {
		flag = "-d";
		position++;
	    }
	    else // DEFAULT E
	    {
		flag = "-e";
		position++;
            }
        }
    }

    int input_size = input.size();
    for (; position < input_size; position++)
    {
        if (input[position] == ")")
        {
            connect.precedence = false;
            position++;
            break;
        }
        if(command == "echo" && input[position] == "\"")
        {
            position++;
            while(input[position] != "\"" && position <= input_size)
            {

                argument += input[position];
                if(!isalnum(input[position].at(0)) && !isalnum(input[position+1].at(0)))
                {

                }
                else
		{
                    argument += " ";
                }
		position++;
            }
            position++;
            break;
        }
        if (input[position] == "#")
        {
            position = input.size();
            break;
        }
        if (input[position] == "&" || input[position] == "|" || input[position] == ";")
	    {
            break;
    	}

        argument += input[position];
        int input_size1 = input.size();
        if (position+1 != input_size1 && command == "echo")
	{
	    //adds spaces when echoing
            argument += " ";
	}
    }
}
void commandifier(string command, string argument, Connector& connect, string flag)
{
    const char * const_command = command.c_str();
    char * char_command = new char[command.size()];
    const char * const_argument = argument.c_str();
    char * char_argument = new char[argument.size()];
    strcpy (char_command, const_command);
    strcpy (char_argument, const_argument);
    char * args[2]; // char pointer array that holds command, and NULL
    char * args1[3]; // char pointer array that holds command, argument, and NULL
    bool no_arg = true;
    bool failed = false;

    if(command == "test" || command == "[")
    {
	test_function(command,flag,argument,connect);
	return;
    }
    // exit command
    if(command == "exit" || argument == "exit")
    {
	exit(1);
    }

    if (argument.size() == 0) // no arguments
    {
        args[0] = char_command; // args will contain the command
        args[1] = NULL;
    }
    else
    {
	no_arg = false; // sets bool no_arg to false
	args1[0] = char_command; // args1 contains command and argument
        args1[1] = char_argument;
        args1[2] = NULL;
    }
    pid_t c_pid, pid; // data type to reporesent process ID's
    int status;
    c_pid = fork(); // create our fork

    if (c_pid < 0) // indicates the fork has fail if its less than 0
    {
        perror("fork failed");
        exit(1);
    }
    else if (c_pid == 0) // in child process
    {
 	if (no_arg) // no argument
	{
        	execvp( args[0], args); // execvp the char pointer array that holds the command only
        	if (execvp(args[0], args) == -1) // if it returns -1 it failed
                {
                    perror("execvp failed");
                    connect.run = false;
                    failed = true;
                }
		exit(3);
	}
	else // with command and argument
	{
		execvp(args1[0], args1); // execvp the char pointer array that holds the command, and argument
		if (execvp(args1[0], args1) == -1) // if it returns -1 it failed
                {
                    perror("execvp failed");
                    connect.run = false;
                    failed = true;
                }
		exit(3);
	}
    }
    else if (c_pid > 0) // parent process
    {
        if((pid = wait(&status)) < 0 )
        {
            perror("wait");
	    exit(3);
        }
        if (WIFEXITED(status))  //Evaluates to a non-zero value if status was returned for a child process that terminated normally.

        {
            if (WEXITSTATUS(status) != 0)
            {
                connect.run = false;
            }
	    else
	    {
		connect.run = true;
            }
        }
       else
        {
            connect.run = true;
        }
     }
}
void test_function(string command, string & flag, string argument, Connector& connect)
{
   char b = '[';
   char c = ']';
   argument.erase(remove(argument.begin(), argument.end(), c ), argument.end());
   argument.erase(remove(argument.begin(), argument.end(), b ), argument.end());
   const char * arg = argument.c_str();
   struct stat sb; // struct for the stat function

   if(flag == "-f")
   {
        if(stat(arg,&sb) == 0)
        {

            connect.run = true;
	    if(S_ISREG(sb.st_mode))
	    {

 		connect.run = true;

	    }
	    else
	    {

		connect.run = false;

	    }
        }
        else
        {
	    perror("stat");
            connect.run = false;
        }
   }
   else if(flag == "-d")
   {
        if(stat(arg,&sb) == 0)
        {
            connect.run = true;
            if(S_ISDIR(sb.st_mode))
            {
            	connect.run = true;

            }
            else
       	    {
             	connect.run = false;

            }
        }
        else
        {
	    perror("stat");
            connect.run = false;
        }

   }
   else
   {
	if(stat(arg,&sb) == 0)
	{
	    connect.run = true;
 	}
	else
	{
	    perror("stat");
	    connect.run = false;
	}
   }
}

<|endoftext|>


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

#pragma once

#include "system/LargeObject.hxx"
#include "util/Compiler.h"

#include <boost/intrusive/list.hpp>

#include <array>
#include <algorithm>

#include <assert.h>
#include <stddef.h>

struct AllocatorStats;
struct RubberObject;
struct RubberTable;

/**
 * The "rubber" memory allocator.  It is a buffer for storing many
 * large objects.  Unlike heap memory, unused areas are given back to
 * the operating system.
 */
class Rubber {
	/**
	 * The sum of all allocation sizes.
	 */
	size_t netto_size = 0;

	/**
	 * The table managing the allocations in the memory map.  At the
	 * same time, this is the pointer to the memory map.
	 */
	LargeObject<RubberTable> table;

	/**
	 * The threshold for each hole list.  The goal is to reduce the cost
	 * of searching a hole that fits.
	 */
	static constexpr size_t HOLE_THRESHOLDS[] = {
		1024 * 1024, 64 * 1024, 32 * 1024, 16 * 1024, 8192, 4096, 2048, 1024, 64, 0
	};

	gcc_const
	static unsigned LookupHoleThreshold(size_t size) noexcept {
		for (unsigned i = 0;; ++i)
			if (size >= HOLE_THRESHOLDS[i])
				return i;
	}

	static constexpr size_t N_HOLE_THRESHOLDS = std::size(HOLE_THRESHOLDS);

	struct Hole final
		: boost::intrusive::list_base_hook<boost::intrusive::link_mode<boost::intrusive::normal_link>> {

		/**
		 * The size of this hole (including the size of this struct).
		 */
		size_t size;

		/**
		 * The allocated objects before and after this hole.
		 */
		unsigned previous_id, next_id;
	};

	typedef boost::intrusive::list<Hole,
				       boost::intrusive::constant_time_size<false>> HoleList;

	/**
	 * A list of all holes in the buffer.  Each array element hosts
	 * its own list with holes at the size of
	 * HOLE_THRESHOLDS[i] or bigger.
	 */
	std::array<HoleList, N_HOLE_THRESHOLDS> holes;

public:
	/**
	 * Throws std::bad_alloc on error.
	 */
	explicit Rubber(size_t _max_size);

	~Rubber() noexcept;

	/**
	 * Controls whether forked child processes inherit the allocator.
	 * This is enabled by default.
	 */
	void ForkCow(bool inherit) noexcept;

	/**
	 * Returns the maximum total size of all allocations.
	 */
	gcc_pure
	size_t GetMaxSize() const noexcept;

	/**
	 * Returns the total size of all allocations.
	 */
	size_t GetNettoSize() const noexcept {
		return netto_size;
	}

	/**
	 * Returns the memory consumed by this object, not including the
	 * allocation table.
	 */
	gcc_pure
	size_t GetBruttoSize() const noexcept;

	gcc_pure
	AllocatorStats GetStats() const noexcept;

	void Compress() noexcept;

	/**
	 * Add a new object with the specified size.  Use Write() to
	 * actually copy data to the object.
	 *
	 * @param size the size, must be positive
	 * @return the object id, or 0 on error
	 */
	unsigned Add(size_t size) noexcept;
	void Remove(unsigned id) noexcept;

	/**
	 * Shrink an object.  The new size must be smaller (or equal) to
	 * the current size.  This is done in-place, possibly leaving a
	 * gap that can only be used again after Compress() has been
	 * called.
	 *
	 * @param new_size the new size, must be positive
	 */
	void Shrink(unsigned id, size_t new_size) noexcept;

	/**
	 * Returns the size of an allocation.  Due to padding, the
	 * returned value may be a bit bigger than the size that was
	 * passed to Add().
	 */
	gcc_pure
	size_t GetSizeOf(unsigned id) const noexcept;

	/**
	 * Return a writable pointer to the object.
	 */
	gcc_pure
	void *Write(unsigned id) noexcept;

	/**
	 * Return a read-only pointer to the object.
	 */
	gcc_pure
	const void *Read(unsigned id) const noexcept;

private:
	gcc_pure
	void *WriteAt(size_t offset) noexcept {
		assert(offset <= table.size());

		return (uint8_t *)table.get() + offset;
	}

	gcc_pure
	const void *ReadAt(size_t offset) const noexcept {
		assert(offset <= table.size());

		return (const uint8_t *)table.get() + offset;
	}

	size_t OffsetOf(const void *p) const noexcept {
		return (const uint8_t *)p - (const uint8_t *)table.get();
	}

	size_t OffsetOf(const Hole &hole) const noexcept {
		return OffsetOf(&hole);
	}

	gcc_pure
	static size_t GetTotalHoleSize(const HoleList &holes) noexcept;

#ifndef NDEBUG
	size_t GetTotalHoleSize() const noexcept;
#endif

	gcc_pure
	static Hole *FindHole(HoleList &holes, size_t size) noexcept;

	gcc_pure
	Hole *FindHole(size_t size) noexcept;

	void AddToHoleList(Hole &hole) noexcept;

	void AddHole(size_t offset, size_t size,
		     unsigned previous_id, unsigned next_id) noexcept;
	void AddHoleAfter(unsigned reference_id,
			  size_t offset, size_t size) noexcept;

	/**
	 * Replace the hole with the specified object.  If there is unused
	 * space after the object, create a new #Hole instance
	 * there.
	 */
	void UseHole(Hole &hole, unsigned id, size_t size) noexcept;

	unsigned AddInHole(Hole &hole, size_t size) noexcept;

	/**
	 * Try to find a hole between two objects, and insert a new object
	 * there.
	 *
	 * @return the object id, or 0 on error
	 */
	unsigned AddInHole(size_t size) noexcept;

	/**
	 * Attempt to move the last allocation into a hole.  This is some kind
	 * of simplified defragmentation.  It attempts to keep the "brutto"
	 * size of this allocator small by filling holes.
	 *
	 * @param max_object_size move it only if it's not larger than this size
	 */
	bool MoveLast(size_t max_object_size) noexcept;

	gcc_pure
	Hole *FindHoleBetween(RubberObject &a, RubberObject &b) noexcept;

	/**
	 * If there is a hole between the two objects, discard it.  This
	 * is used to remove holes at the end of the mmap when the last
	 * object got removed.
	 */
	void DiscardHoleBetween(RubberObject &a, RubberObject &b) noexcept;

	/**
	 * The given object shall disappear at its current offset.  This
	 * method will replace it with a #Hole instance, or will
	 * grow/merge existing #Hole instances surrounding it.
	 *
	 * This method will not remove the #RubberObject from the table /
	 * linked list, nor will it update the netto size.  It assumes
	 * that the #RubberObject has already been removed from the linked
	 * list.  It will corrupt data previously allocated by the
	 * #RubberObject.
	 */
	void ReplaceWithHole(RubberObject &o,
			     unsigned previous_id, unsigned next_id) noexcept;

	void MoveData(RubberObject &o, size_t new_offset) noexcept;

	HoleList &GetHoleList(size_t size) noexcept {
		return holes[LookupHoleThreshold(size)];
	}

	HoleList &GetHoleList(Hole &hole) noexcept {
		return GetHoleList(hole.size);
	}

	void RemoveHole(Hole &hole) noexcept {
		GetHoleList(hole).erase(HoleList::s_iterator_to(hole));
	}
};

/**
 * An allocation from a #Rubber instance.  This class "owns" the
 * allocation and frees it automatically.
 */
class RubberAllocation {
	Rubber *rubber = nullptr;
	unsigned id = 0;

public:
	RubberAllocation() = default;

	RubberAllocation(Rubber &_rubber, unsigned _id) noexcept
		:rubber(&_rubber), id(_id) {}

	RubberAllocation(RubberAllocation &&src) noexcept
		:rubber(std::exchange(src.rubber, nullptr)),
		 id(std::exchange(src.id, 0)) {}

	~RubberAllocation() {
		if (id != 0)
			rubber->Remove(id);
	}

	RubberAllocation &operator=(RubberAllocation &&src) noexcept {
		using std::swap;
		swap(rubber, src.rubber);
		swap(id, src.id);
		return *this;
	}

	operator bool() const noexcept {
		return id != 0;
	}

	Rubber &GetRubber() const noexcept {
		assert(*this);
		return *rubber;
	}

	unsigned GetId() const noexcept {
		assert(*this);
		return id;
	}

	void Shrink(size_t new_size) noexcept {
		assert(*this);

		rubber->Shrink(id, new_size);
	}

	void *Write() noexcept {
		assert(*this);

		return rubber->Write(id);
	}

	const void *Read() noexcept {
		assert(*this);

		return rubber->Read(id);
	}
};
<commit_msg>rubber: use IntrusiveList<><commit_after>/*
 * Copyright 2007-2020 CM4all GmbH
 * All rights reserved.
 *
 * author: Max Kellermann <mk@cm4all.com>
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * - Redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer.
 *
 * - Redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the
 * distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
 * FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
 * OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#pragma once

#include "system/LargeObject.hxx"
#include "util/Compiler.h"
#include "util/IntrusiveList.hxx"

#include <array>
#include <algorithm>

#include <assert.h>
#include <stddef.h>

struct AllocatorStats;
struct RubberObject;
struct RubberTable;

/**
 * The "rubber" memory allocator.  It is a buffer for storing many
 * large objects.  Unlike heap memory, unused areas are given back to
 * the operating system.
 */
class Rubber {
	/**
	 * The sum of all allocation sizes.
	 */
	size_t netto_size = 0;

	/**
	 * The table managing the allocations in the memory map.  At the
	 * same time, this is the pointer to the memory map.
	 */
	LargeObject<RubberTable> table;

	/**
	 * The threshold for each hole list.  The goal is to reduce the cost
	 * of searching a hole that fits.
	 */
	static constexpr size_t HOLE_THRESHOLDS[] = {
		1024 * 1024, 64 * 1024, 32 * 1024, 16 * 1024, 8192, 4096, 2048, 1024, 64, 0
	};

	gcc_const
	static unsigned LookupHoleThreshold(size_t size) noexcept {
		for (unsigned i = 0;; ++i)
			if (size >= HOLE_THRESHOLDS[i])
				return i;
	}

	static constexpr size_t N_HOLE_THRESHOLDS = std::size(HOLE_THRESHOLDS);

	struct Hole final : IntrusiveListHook {
		/**
		 * The size of this hole (including the size of this struct).
		 */
		size_t size;

		/**
		 * The allocated objects before and after this hole.
		 */
		unsigned previous_id, next_id;
	};

	using HoleList = IntrusiveList<Hole>;

	/**
	 * A list of all holes in the buffer.  Each array element hosts
	 * its own list with holes at the size of
	 * HOLE_THRESHOLDS[i] or bigger.
	 */
	std::array<HoleList, N_HOLE_THRESHOLDS> holes;

public:
	/**
	 * Throws std::bad_alloc on error.
	 */
	explicit Rubber(size_t _max_size);

	~Rubber() noexcept;

	/**
	 * Controls whether forked child processes inherit the allocator.
	 * This is enabled by default.
	 */
	void ForkCow(bool inherit) noexcept;

	/**
	 * Returns the maximum total size of all allocations.
	 */
	gcc_pure
	size_t GetMaxSize() const noexcept;

	/**
	 * Returns the total size of all allocations.
	 */
	size_t GetNettoSize() const noexcept {
		return netto_size;
	}

	/**
	 * Returns the memory consumed by this object, not including the
	 * allocation table.
	 */
	gcc_pure
	size_t GetBruttoSize() const noexcept;

	gcc_pure
	AllocatorStats GetStats() const noexcept;

	void Compress() noexcept;

	/**
	 * Add a new object with the specified size.  Use Write() to
	 * actually copy data to the object.
	 *
	 * @param size the size, must be positive
	 * @return the object id, or 0 on error
	 */
	unsigned Add(size_t size) noexcept;
	void Remove(unsigned id) noexcept;

	/**
	 * Shrink an object.  The new size must be smaller (or equal) to
	 * the current size.  This is done in-place, possibly leaving a
	 * gap that can only be used again after Compress() has been
	 * called.
	 *
	 * @param new_size the new size, must be positive
	 */
	void Shrink(unsigned id, size_t new_size) noexcept;

	/**
	 * Returns the size of an allocation.  Due to padding, the
	 * returned value may be a bit bigger than the size that was
	 * passed to Add().
	 */
	gcc_pure
	size_t GetSizeOf(unsigned id) const noexcept;

	/**
	 * Return a writable pointer to the object.
	 */
	gcc_pure
	void *Write(unsigned id) noexcept;

	/**
	 * Return a read-only pointer to the object.
	 */
	gcc_pure
	const void *Read(unsigned id) const noexcept;

private:
	gcc_pure
	void *WriteAt(size_t offset) noexcept {
		assert(offset <= table.size());

		return (uint8_t *)table.get() + offset;
	}

	gcc_pure
	const void *ReadAt(size_t offset) const noexcept {
		assert(offset <= table.size());

		return (const uint8_t *)table.get() + offset;
	}

	size_t OffsetOf(const void *p) const noexcept {
		return (const uint8_t *)p - (const uint8_t *)table.get();
	}

	size_t OffsetOf(const Hole &hole) const noexcept {
		return OffsetOf(&hole);
	}

	gcc_pure
	static size_t GetTotalHoleSize(const HoleList &holes) noexcept;

#ifndef NDEBUG
	size_t GetTotalHoleSize() const noexcept;
#endif

	gcc_pure
	static Hole *FindHole(HoleList &holes, size_t size) noexcept;

	gcc_pure
	Hole *FindHole(size_t size) noexcept;

	void AddToHoleList(Hole &hole) noexcept;

	void AddHole(size_t offset, size_t size,
		     unsigned previous_id, unsigned next_id) noexcept;
	void AddHoleAfter(unsigned reference_id,
			  size_t offset, size_t size) noexcept;

	/**
	 * Replace the hole with the specified object.  If there is unused
	 * space after the object, create a new #Hole instance
	 * there.
	 */
	void UseHole(Hole &hole, unsigned id, size_t size) noexcept;

	unsigned AddInHole(Hole &hole, size_t size) noexcept;

	/**
	 * Try to find a hole between two objects, and insert a new object
	 * there.
	 *
	 * @return the object id, or 0 on error
	 */
	unsigned AddInHole(size_t size) noexcept;

	/**
	 * Attempt to move the last allocation into a hole.  This is some kind
	 * of simplified defragmentation.  It attempts to keep the "brutto"
	 * size of this allocator small by filling holes.
	 *
	 * @param max_object_size move it only if it's not larger than this size
	 */
	bool MoveLast(size_t max_object_size) noexcept;

	gcc_pure
	Hole *FindHoleBetween(RubberObject &a, RubberObject &b) noexcept;

	/**
	 * If there is a hole between the two objects, discard it.  This
	 * is used to remove holes at the end of the mmap when the last
	 * object got removed.
	 */
	void DiscardHoleBetween(RubberObject &a, RubberObject &b) noexcept;

	/**
	 * The given object shall disappear at its current offset.  This
	 * method will replace it with a #Hole instance, or will
	 * grow/merge existing #Hole instances surrounding it.
	 *
	 * This method will not remove the #RubberObject from the table /
	 * linked list, nor will it update the netto size.  It assumes
	 * that the #RubberObject has already been removed from the linked
	 * list.  It will corrupt data previously allocated by the
	 * #RubberObject.
	 */
	void ReplaceWithHole(RubberObject &o,
			     unsigned previous_id, unsigned next_id) noexcept;

	void MoveData(RubberObject &o, size_t new_offset) noexcept;

	HoleList &GetHoleList(size_t size) noexcept {
		return holes[LookupHoleThreshold(size)];
	}

	HoleList &GetHoleList(Hole &hole) noexcept {
		return GetHoleList(hole.size);
	}

	void RemoveHole(Hole &hole) noexcept {
		hole.unlink();
	}
};

/**
 * An allocation from a #Rubber instance.  This class "owns" the
 * allocation and frees it automatically.
 */
class RubberAllocation {
	Rubber *rubber = nullptr;
	unsigned id = 0;

public:
	RubberAllocation() = default;

	RubberAllocation(Rubber &_rubber, unsigned _id) noexcept
		:rubber(&_rubber), id(_id) {}

	RubberAllocation(RubberAllocation &&src) noexcept
		:rubber(std::exchange(src.rubber, nullptr)),
		 id(std::exchange(src.id, 0)) {}

	~RubberAllocation() {
		if (id != 0)
			rubber->Remove(id);
	}

	RubberAllocation &operator=(RubberAllocation &&src) noexcept {
		using std::swap;
		swap(rubber, src.rubber);
		swap(id, src.id);
		return *this;
	}

	operator bool() const noexcept {
		return id != 0;
	}

	Rubber &GetRubber() const noexcept {
		assert(*this);
		return *rubber;
	}

	unsigned GetId() const noexcept {
		assert(*this);
		return id;
	}

	void Shrink(size_t new_size) noexcept {
		assert(*this);

		rubber->Shrink(id, new_size);
	}

	void *Write() noexcept {
		assert(*this);

		return rubber->Write(id);
	}

	const void *Read() noexcept {
		assert(*this);

		return rubber->Read(id);
	}
};
<|endoftext|>


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

This file is part of the QtMediaHub project on http://www.gitorious.org.

Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).*
All rights reserved.

Contact:  Nokia Corporation (qt-info@nokia.com)**

You may use this file under the terms of the BSD license as follows:

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

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

#include "mediamodel.h"
#include "mediascanner.h"
#include "dbreader.h"
#include "backend.h"

#define DEBUG if (1) qDebug() << __PRETTY_FUNCTION__

MediaModel::MediaModel(QObject *parent)
    : QAbstractItemModel(parent), m_loading(false), m_loaded(false), m_reader(0), m_readerThread(0)
{
}

MediaModel::~MediaModel()
{
}

QString MediaModel::part() const
{
    return m_structure.split("|").value(m_cursor.length());
}

QString MediaModel::mediaType() const
{
    return m_mediaType;
}

void MediaModel::setMediaType(const QString &type)
{
    if (type == m_mediaType)
        return;

    DEBUG << type;

    m_mediaType = type;
    emit mediaTypeChanged();

    // Add the fields of the table as role names
    QSqlDriver *driver = Backend::instance()->mediaDatabase().driver();
    QSqlRecord record = driver->record(m_mediaType);
    if (record.isEmpty())
        qWarning() << "Table " << type << " is not valid it seems";

    QHash<int, QByteArray> hash = roleNames();
    hash.insert(DotDotRole, "dotdot");
    hash.insert(IsLeafRole, "isLeaf");
    hash.insert(ModelIndexRole,"modelIndex");
    hash.insert(PreviewUrlRole, "previewUrl");

    for (int i = 0; i < record.count(); i++) {
        hash.insert(FieldRolesBegin + i, record.fieldName(i).toUtf8());
    }

    setRoleNames(hash);

    initialize();
}

void MediaModel::addSearchPath(const QString &path, const QString &name)
{
    if (m_mediaType.isEmpty()) {
        qWarning() << m_mediaType << " is not set yet";
        return;
    }

    QMetaObject::invokeMethod(MediaScanner::instance(), "addSearchPath", Qt::QueuedConnection,
                              Q_ARG(QString, m_mediaType), Q_ARG(QString, path), Q_ARG(QString, name));
}

void MediaModel::removeSearchPath(int index)
{
    Q_UNUSED(index);
}

QString MediaModel::structure() const
{
    return m_structure;
}

void MediaModel::setStructure(const QString &str)
{
    if (str == m_structure)
        return;
    DEBUG << str;
    m_structure = str;
    m_layoutInfo.clear();
    foreach(const QString &part, m_structure.split("|"))
        m_layoutInfo.append(part.split(","));

    initialize();

    emit structureChanged();
}

void MediaModel::enter(int index)
{
    if (m_cursor.count() + 1 == m_layoutInfo.count() && index != 0 /* up on leaf node is OK */) {
        DEBUG << "Refusing to enter leaf node";
        return;
    }

    if (index == 0 && !m_cursor.isEmpty()) {
        back();
        return;
    }

    DEBUG << "Entering " << index;
    m_cursor.append(m_data[index]);
    initialize();
    emit partChanged();
}

void MediaModel::back()
{
    m_cursor.removeLast();
    initialize();
    emit partChanged();
}

QVariant MediaModel::data(const QModelIndex &index, int role) const
{
    if (!index.isValid())
        return QVariant();

    if (role == ModelIndexRole)
        return qVariantFromValue(index);

    return m_data.value(index.row()).value(role);
}

QModelIndex MediaModel::index(int row, int col, const QModelIndex &parent) const
{
    if (parent.isValid())
        return QModelIndex();

    return createIndex(row, col);
}

QModelIndex MediaModel::parent(const QModelIndex &idx) const
{
    Q_UNUSED(idx);
    return QModelIndex();
}

int MediaModel::rowCount(const QModelIndex &parent) const
{
    if (parent.isValid())
        return 0;
    return m_data.count();
}

int MediaModel::columnCount(const QModelIndex &idx) const
{
    Q_UNUSED(idx);
    return 1;
}

bool MediaModel::hasChildren(const QModelIndex &parent) const
{
    return !parent.isValid();
}

bool MediaModel::canFetchMore(const QModelIndex &parent) const
{
    if (parent.isValid() || m_mediaType.isEmpty() || m_layoutInfo.isEmpty()) {
        DEBUG << "false " << parent.isValid() << m_mediaType.isEmpty() << m_layoutInfo.isEmpty();
        return false;
    }
    DEBUG << "true";
    return !m_loading && !m_loaded;
}

void MediaModel::fetchMore(const QModelIndex &parent)
{
    if (!canFetchMore(parent))
        return;

    DEBUG << "";

    m_loading = true;

    QSqlQuery q = buildQuery();
    DEBUG << q.lastQuery();
    QMetaObject::invokeMethod(m_reader, "execute", Qt::QueuedConnection, Q_ARG(QSqlQuery, q));
}

void MediaModel::initialize()
{
    DEBUG << "";

    DbReader *newReader = new DbReader;
    if (m_reader) {
        disconnect(m_reader, 0, this, 0);
        m_reader->stop();
        m_reader->deleteLater();
    }
    m_reader = newReader;

    if (!m_readerThread) {
        m_readerThread = new QThread(this);
        m_readerThread->start();
    }
    m_reader->moveToThread(m_readerThread);
    QMetaObject::invokeMethod(m_reader, "initialize", Q_ARG(QSqlDatabase, Backend::instance()->mediaDatabase()));
    connect(m_reader, SIGNAL(dataReady(DbReader *, QList<QSqlRecord>, void *)),
            this, SLOT(handleDataReady(DbReader *, QList<QSqlRecord>, void *)));

    beginResetModel();
    m_loading = m_loaded = false;
    m_data.clear();
    endResetModel();
}

void MediaModel::handleDataReady(DbReader *reader, const QList<QSqlRecord> &records, void *node)
{
    Q_ASSERT(reader == m_reader);
    Q_UNUSED(reader);
    Q_UNUSED(node);

    DEBUG << "Received response from db of size " << records.size();

    if (records.isEmpty())
        return;

    if (!m_cursor.isEmpty()) {
        beginInsertRows(QModelIndex(), 0, records.count());
        QHash<int, QVariant> data;
        data.insert(Qt::DisplayRole, tr(".."));
        data.insert(DotDotRole, true);
        data.insert(IsLeafRole, false);
        m_data.append(data);
    } else {
        beginInsertRows(QModelIndex(), 0, records.count() - 1);
    }

    const bool isLeaf = m_cursor.count() + 1 == m_layoutInfo.count();

    QSqlDriver *driver = Backend::instance()->mediaDatabase().driver();
    const QSqlRecord tableRecord = driver->record(m_mediaType);

    for (int i = 0; i < records.count(); i++) {
        QHash<int, QVariant> data;
        for (int j = 0; j < records[i].count(); j++) {
            int idx = tableRecord.indexOf(records[i].fieldName(j));
            data.insert(FieldRolesBegin + idx, records[i].value(j));
        }

        // Provide 'display' role as , separated values
        QStringList cols = m_layoutInfo.value(m_cursor.count());
        QStringList displayString;
        for (int j = 0; j < cols.count(); j++) {
            displayString << records[i].value(cols[j]).toString();
        }
        data.insert(Qt::DisplayRole, displayString.join(", "));
        data.insert(DotDotRole, false);
        data.insert(IsLeafRole, isLeaf);

        data.insert(PreviewUrlRole, records[i].value("thumbnail").toString());

        m_data.append(data);
    }

    m_loading = false;
    m_loaded = true;

    endInsertRows();
}

void MediaModel::handleDatabaseUpdated(const QList<QSqlRecord> &records)
{
    Q_UNUSED(records);
    // not implemented yet
}

QSqlQuery MediaModel::buildQuery() const
{
    QSqlDriver *driver = Backend::instance()->mediaDatabase().driver();

    QStringList escapedCurParts;
    QStringList curParts = m_layoutInfo[m_cursor.count()];
    for (int i = 0; i < curParts.count(); i++)
        escapedCurParts.append(driver->escapeIdentifier(curParts[i], QSqlDriver::FieldName));
    QString escapedCurPart = escapedCurParts.join(",");

    QSqlQuery query(Backend::instance()->mediaDatabase());
    query.setForwardOnly(true);

    QStringList placeHolders;
    const bool lastPart = m_cursor.count() == m_layoutInfo.count()-1;
    QString queryString;
    // SQLite allows us to select columns that are not present in the GROUP BY. We use this feature
    // to select thumbnails for non-leaf nodes
    queryString.append("SELECT *");
    queryString.append(" FROM " + driver->escapeIdentifier(m_mediaType, QSqlDriver::TableName));

    if (!m_cursor.isEmpty()) {
        QStringList where;
        const QSqlRecord tableRecord = driver->record(m_mediaType);
        for (int i = 0; i < m_cursor.count(); i++) {
            QStringList subParts = m_layoutInfo[i];
            for (int j = 0; j < subParts.count(); j++) {
                where.append(subParts[j] + " = ?");
                const int role = FieldRolesBegin + tableRecord.indexOf(subParts[j]);
                placeHolders << m_cursor[i].value(role).toString();
            }
        }

        queryString.append(" WHERE " + where.join(" AND "));
    }

    if (!lastPart)
        queryString.append(" GROUP BY " + escapedCurPart);

    queryString.append(" ORDER BY " + escapedCurPart);

    query.prepare(queryString);

    foreach(const QString &placeHolder, placeHolders)
        query.addBindValue(placeHolder);

    return query;
}

<commit_msg>Fix the debug<commit_after>/****************************************************************************

This file is part of the QtMediaHub project on http://www.gitorious.org.

Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).*
All rights reserved.

Contact:  Nokia Corporation (qt-info@nokia.com)**

You may use this file under the terms of the BSD license as follows:

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

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

#include "mediamodel.h"
#include "mediascanner.h"
#include "dbreader.h"
#include "backend.h"

#define DEBUG if (1) qDebug() << __PRETTY_FUNCTION__

MediaModel::MediaModel(QObject *parent)
    : QAbstractItemModel(parent), m_loading(false), m_loaded(false), m_reader(0), m_readerThread(0)
{
}

MediaModel::~MediaModel()
{
}

QString MediaModel::part() const
{
    return m_structure.split("|").value(m_cursor.length());
}

QString MediaModel::mediaType() const
{
    return m_mediaType;
}

void MediaModel::setMediaType(const QString &type)
{
    if (type == m_mediaType)
        return;

    DEBUG << type;

    m_mediaType = type;
    emit mediaTypeChanged();

    // Add the fields of the table as role names
    QSqlDriver *driver = Backend::instance()->mediaDatabase().driver();
    QSqlRecord record = driver->record(m_mediaType);
    if (record.isEmpty())
        qWarning() << "Table " << type << " is not valid it seems";

    QHash<int, QByteArray> hash = roleNames();
    hash.insert(DotDotRole, "dotdot");
    hash.insert(IsLeafRole, "isLeaf");
    hash.insert(ModelIndexRole,"modelIndex");
    hash.insert(PreviewUrlRole, "previewUrl");

    for (int i = 0; i < record.count(); i++) {
        hash.insert(FieldRolesBegin + i, record.fieldName(i).toUtf8());
    }

    setRoleNames(hash);

    initialize();
}

void MediaModel::addSearchPath(const QString &path, const QString &name)
{
    if (m_mediaType.isEmpty()) {
        qWarning() << m_mediaType << " is not set yet";
        return;
    }

    QMetaObject::invokeMethod(MediaScanner::instance(), "addSearchPath", Qt::QueuedConnection,
                              Q_ARG(QString, m_mediaType), Q_ARG(QString, path), Q_ARG(QString, name));
}

void MediaModel::removeSearchPath(int index)
{
    Q_UNUSED(index);
}

QString MediaModel::structure() const
{
    return m_structure;
}

void MediaModel::setStructure(const QString &str)
{
    if (str == m_structure)
        return;
    DEBUG << str;
    m_structure = str;
    m_layoutInfo.clear();
    foreach(const QString &part, m_structure.split("|"))
        m_layoutInfo.append(part.split(","));

    initialize();

    emit structureChanged();
}

void MediaModel::enter(int index)
{
    if (m_cursor.count() + 1 == m_layoutInfo.count() && index != 0 /* up on leaf node is OK */) {
        DEBUG << "Refusing to enter leaf node";
        return;
    }

    if (index == 0 && !m_cursor.isEmpty()) {
        back();
        return;
    }

    DEBUG << "Entering " << index;
    m_cursor.append(m_data[index]);
    initialize();
    emit partChanged();
}

void MediaModel::back()
{
    m_cursor.removeLast();
    initialize();
    emit partChanged();
}

QVariant MediaModel::data(const QModelIndex &index, int role) const
{
    if (!index.isValid())
        return QVariant();

    if (role == ModelIndexRole)
        return qVariantFromValue(index);

    return m_data.value(index.row()).value(role);
}

QModelIndex MediaModel::index(int row, int col, const QModelIndex &parent) const
{
    if (parent.isValid())
        return QModelIndex();

    return createIndex(row, col);
}

QModelIndex MediaModel::parent(const QModelIndex &idx) const
{
    Q_UNUSED(idx);
    return QModelIndex();
}

int MediaModel::rowCount(const QModelIndex &parent) const
{
    if (parent.isValid())
        return 0;
    return m_data.count();
}

int MediaModel::columnCount(const QModelIndex &idx) const
{
    Q_UNUSED(idx);
    return 1;
}

bool MediaModel::hasChildren(const QModelIndex &parent) const
{
    return !parent.isValid();
}

bool MediaModel::canFetchMore(const QModelIndex &parent) const
{
    if (parent.isValid() || m_mediaType.isEmpty() || m_layoutInfo.isEmpty()) {
        DEBUG << "false " << parent.isValid() << m_mediaType.isEmpty() << m_layoutInfo.isEmpty();
        return false;
    }
    DEBUG << !m_loading && !m_loaded;
    return !m_loading && !m_loaded;
}

void MediaModel::fetchMore(const QModelIndex &parent)
{
    if (!canFetchMore(parent))
        return;

    DEBUG << "";

    m_loading = true;

    QSqlQuery q = buildQuery();
    DEBUG << q.lastQuery();
    QMetaObject::invokeMethod(m_reader, "execute", Qt::QueuedConnection, Q_ARG(QSqlQuery, q));
}

void MediaModel::initialize()
{
    DEBUG << "";

    DbReader *newReader = new DbReader;
    if (m_reader) {
        disconnect(m_reader, 0, this, 0);
        m_reader->stop();
        m_reader->deleteLater();
    }
    m_reader = newReader;

    if (!m_readerThread) {
        m_readerThread = new QThread(this);
        m_readerThread->start();
    }
    m_reader->moveToThread(m_readerThread);
    QMetaObject::invokeMethod(m_reader, "initialize", Q_ARG(QSqlDatabase, Backend::instance()->mediaDatabase()));
    connect(m_reader, SIGNAL(dataReady(DbReader *, QList<QSqlRecord>, void *)),
            this, SLOT(handleDataReady(DbReader *, QList<QSqlRecord>, void *)));

    beginResetModel();
    m_loading = m_loaded = false;
    m_data.clear();
    endResetModel();
}

void MediaModel::handleDataReady(DbReader *reader, const QList<QSqlRecord> &records, void *node)
{
    Q_ASSERT(reader == m_reader);
    Q_UNUSED(reader);
    Q_UNUSED(node);

    DEBUG << "Received response from db of size " << records.size();

    if (records.isEmpty())
        return;

    if (!m_cursor.isEmpty()) {
        beginInsertRows(QModelIndex(), 0, records.count());
        QHash<int, QVariant> data;
        data.insert(Qt::DisplayRole, tr(".."));
        data.insert(DotDotRole, true);
        data.insert(IsLeafRole, false);
        m_data.append(data);
    } else {
        beginInsertRows(QModelIndex(), 0, records.count() - 1);
    }

    const bool isLeaf = m_cursor.count() + 1 == m_layoutInfo.count();

    QSqlDriver *driver = Backend::instance()->mediaDatabase().driver();
    const QSqlRecord tableRecord = driver->record(m_mediaType);

    for (int i = 0; i < records.count(); i++) {
        QHash<int, QVariant> data;
        for (int j = 0; j < records[i].count(); j++) {
            int idx = tableRecord.indexOf(records[i].fieldName(j));
            data.insert(FieldRolesBegin + idx, records[i].value(j));
        }

        // Provide 'display' role as , separated values
        QStringList cols = m_layoutInfo.value(m_cursor.count());
        QStringList displayString;
        for (int j = 0; j < cols.count(); j++) {
            displayString << records[i].value(cols[j]).toString();
        }
        data.insert(Qt::DisplayRole, displayString.join(", "));
        data.insert(DotDotRole, false);
        data.insert(IsLeafRole, isLeaf);

        data.insert(PreviewUrlRole, records[i].value("thumbnail").toString());

        m_data.append(data);
    }

    m_loading = false;
    m_loaded = true;

    endInsertRows();
}

void MediaModel::handleDatabaseUpdated(const QList<QSqlRecord> &records)
{
    Q_UNUSED(records);
    // not implemented yet
}

QSqlQuery MediaModel::buildQuery() const
{
    QSqlDriver *driver = Backend::instance()->mediaDatabase().driver();

    QStringList escapedCurParts;
    QStringList curParts = m_layoutInfo[m_cursor.count()];
    for (int i = 0; i < curParts.count(); i++)
        escapedCurParts.append(driver->escapeIdentifier(curParts[i], QSqlDriver::FieldName));
    QString escapedCurPart = escapedCurParts.join(",");

    QSqlQuery query(Backend::instance()->mediaDatabase());
    query.setForwardOnly(true);

    QStringList placeHolders;
    const bool lastPart = m_cursor.count() == m_layoutInfo.count()-1;
    QString queryString;
    // SQLite allows us to select columns that are not present in the GROUP BY. We use this feature
    // to select thumbnails for non-leaf nodes
    queryString.append("SELECT *");
    queryString.append(" FROM " + driver->escapeIdentifier(m_mediaType, QSqlDriver::TableName));

    if (!m_cursor.isEmpty()) {
        QStringList where;
        const QSqlRecord tableRecord = driver->record(m_mediaType);
        for (int i = 0; i < m_cursor.count(); i++) {
            QStringList subParts = m_layoutInfo[i];
            for (int j = 0; j < subParts.count(); j++) {
                where.append(subParts[j] + " = ?");
                const int role = FieldRolesBegin + tableRecord.indexOf(subParts[j]);
                placeHolders << m_cursor[i].value(role).toString();
            }
        }

        queryString.append(" WHERE " + where.join(" AND "));
    }

    if (!lastPart)
        queryString.append(" GROUP BY " + escapedCurPart);

    queryString.append(" ORDER BY " + escapedCurPart);

    query.prepare(queryString);

    foreach(const QString &placeHolder, placeHolders)
        query.addBindValue(placeHolder);

    return query;
}

<|endoftext|>


<commit_before>/*
 * This file is part of accounts-ui
 *
 * Copyright (C) 2009-2010 Nokia Corporation.
 *
 * Contact: Alberto Mardegan <alberto.mardegan@nokia.com>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public License
 * version 2.1 as published by the Free Software Foundation.
 *
 * This library is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA
 */

//project
#include "service-settings-widget.h"
#include "abstract-service-setup-context.h"
#include "settings-page.h"
#include "service-helper.h"
#include "basic-service-widget.h"

//M
#include <MBasicListItem>
#include <MLayout>
#include <MLinearLayoutPolicy>
#include <MGridLayoutPolicy>
#include <MLabel>
#include <MButton>
#include <MLocale>
#include <MImageWidget>

//Qt
#include <QDebug>
#include <QDomElement>

namespace AccountsUI {

ServiceSettingsWidgetListItem::ServiceSettingsWidgetListItem(QGraphicsWidget *parent)
        : MBasicListItem(MBasicListItem::IconWithTitleAndSubtitle, parent)
{
    setStyleName("CommonLargePanelInverted");
    setObjectName("wgServiceSettingsWidgetListItem");

    horizontalLayout = new MLayout(this);

    horizontalLayout->setContentsMargins(0, 0, 0, 0);
    MLinearLayoutPolicy *horizontalLayoutPolicy
            = new MLinearLayoutPolicy(horizontalLayout, Qt::Horizontal);
    horizontalLayoutPolicy->setSpacing(0);

    MLayout *titleSubtitleLayout = new MLayout(horizontalLayout);
    titleSubtitleLayout->setContentsMargins(0, 0, 0, 0);
    MLinearLayoutPolicy *titleSubtitleLayoutPolicy = new MLinearLayoutPolicy(titleSubtitleLayout, Qt::Vertical);
    titleSubtitleLayoutPolicy->setSpacing(0);

    titleLabelWidget()->setStyleName("CommonTitleInverted");
    titleSubtitleLayoutPolicy->addItem(titleLabelWidget(), Qt::AlignLeft | Qt::AlignTop);
    subtitleLabelWidget()->setStyleName("CommonSubTitleInverted");
    titleSubtitleLayoutPolicy->addItem(subtitleLabelWidget(), Qt::AlignLeft | Qt::AlignTop);

    horizontalLayoutPolicy->addItem(titleSubtitleLayout, Qt::AlignLeft | Qt::AlignCenter);

    subtitleLabelWidget()->setWordWrap(true);
    subtitleLabelWidget()->setWrapMode(QTextOption::WrapAtWordBoundaryOrAnywhere);
    subtitleLabelWidget()->setTextElide(false);

    MImageWidget *image = imageWidget();
    image->setImage("icon-m-common-drilldown-arrow-inverse");
    image->setStyleName("CommonDrillDownIcon");
    image->setObjectName("wgServiceSettingsWidgetDrillDownIcon");
    image->setVisible(false);
    horizontalLayoutPolicy->addItem(image, Qt::AlignCenter);
}

ServiceSettingsWidgetListItem::~ServiceSettingsWidgetListItem()
{}

QGraphicsLayout *ServiceSettingsWidgetListItem::createLayout()
{
    return horizontalLayout;
}

void ServiceSettingsWidgetListItem::showDrillDownButton()
{
    setStyleName("CommonLargePanelInverted");
    imageWidget()->setVisible(true);
}

class ServiceSettingsWidgetPrivate
{
public:
    ServiceSettingsWidgetPrivate()
        : context(0)
         ,enableServiceButton(0)
    {}

    ~ServiceSettingsWidgetPrivate()
    {}

    AbstractServiceSetupContext *context;
    MButton *enableServiceButton;
};

ServiceSettingsWidget::ServiceSettingsWidget(AbstractServiceSetupContext *context,
                                             QGraphicsItem *parent,
                                             int settingsConf,
                                             bool enabled)
    : MWidget(parent),
    d_ptr(new ServiceSettingsWidgetPrivate())
{
    Q_D(ServiceSettingsWidget);

    d->context = context;
    QString catalog = d->context->service()->trCatalog();
    if (!catalog.isEmpty()) {
         MLocale locale;
         locale.installTrCatalog(catalog);
         MLocale::setDefault(locale);
    }

    MLayout *containerMainLayout = new MLayout(this);
    MLinearLayoutPolicy *mainPolicy =
        new MLinearLayoutPolicy(containerMainLayout, Qt::Vertical);
    mainPolicy->setSpacing(0);
    mainPolicy->setContentsMargins(0,0,0,0);

    MWidget *upperWidget = new MWidget(this);
    MLayout *upperLayout = new MLayout(upperWidget);
    MLinearLayoutPolicy *containerMainPolicy =
        new MLinearLayoutPolicy(upperLayout, Qt::Horizontal);
    containerMainPolicy->setSpacing(0);
    containerMainPolicy->setContentsMargins(0,0,0,0);
    ServiceSettingsWidgetListItem *serviceInfoList = 0;
    BasicServiceWidget *serviceInfoLayout = 0;

    if (settingsConf & EnableButton) {
        if (context) {
            /* Dummy layout for the enable service button
             * The layout's top margin is compensating for the top
             * margin of the title label in the ServiceSettingsWidgetListItem
             * CommonTitleInverted - styling issue.
             *
             * TODO - Remove when styling will be fixed. */
            MLayout *enableServiceButtonLayout = new MLayout;
            MLinearLayoutPolicy *enableServiceButtonPolicy =
                new MLinearLayoutPolicy(enableServiceButtonLayout, Qt::Horizontal);
            enableServiceButtonPolicy->setSpacing(0);
            enableServiceButtonPolicy->setContentsMargins(0, 15, 0, 0);

            d->enableServiceButton = new MButton(this);
            d->enableServiceButton->setViewType(MButton::switchType);
            d->enableServiceButton->setStyleName("CommonLeftSwitchInverted");
            d->enableServiceButton->setObjectName("wgServiceSettingsWidgetServiceButton");
            d->enableServiceButton->setCheckable(true);
            enableServiceButtonPolicy->addItem(d->enableServiceButton);

            ServiceHelper *serviceHelper =
                new ServiceHelper(const_cast<Accounts::Service*>(context->service()), this);
            QString subTitle = serviceHelper->description();

            if (settingsConf != (MandatorySettings | EnableButton)) {
                if (!serviceHelper->shortDescription().isEmpty())
                    subTitle = serviceHelper->shortDescription();
            }

            MWidget *widget = context->widget(0);
            MWidget *serviceInfo = 0;
            if (widget) {
                serviceInfoList = new ServiceSettingsWidgetListItem();
                serviceInfoList->setTitle(serviceHelper->prettyName());
                serviceInfoList->setSubtitle(subTitle);
                serviceInfo = serviceInfoList;
            } else {
                serviceInfoLayout = new BasicServiceWidget(BasicServiceWidget::TitleAndSubtitle, this);
                serviceInfoLayout->createLayout();
                serviceInfoLayout->setTitle(serviceHelper->prettyName());
                serviceInfoLayout->setSubtitle(subTitle);
                serviceInfo = serviceInfoLayout;
            }

            /*
             * no signals during widget creation
             * */
            d->enableServiceButton->setChecked(enabled);
            connect(d->enableServiceButton, SIGNAL(toggled(bool)), this, SLOT(enabled(bool)));

            containerMainPolicy->addItem(enableServiceButtonLayout, Qt::AlignRight | Qt::AlignTop);
            containerMainPolicy->addItem(serviceInfo, Qt::AlignLeft | Qt::AlignTop);

            mainPolicy->addItem(upperWidget);
        }
    }

    if (context) {
        MWidget *widget = context->widget(0, (settingsConf & NonMandatorySettings));

        if (widget) {
            if (serviceInfoList) {
                serviceInfoList->showDrillDownButton();
                connect(serviceInfoList, SIGNAL(clicked()),
                        this, SLOT(openSettingsPage()));
            }

            if ((settingsConf & NonMandatorySettings) ||
                (settingsConf & MandatorySettings)) {
                    mainPolicy->addItem(widget);
            }
        }
    }
}

ServiceSettingsWidget::~ServiceSettingsWidget()
{
    delete d_ptr;
}

void ServiceSettingsWidget::enabled(bool enabled)
{
    Q_D(ServiceSettingsWidget);
    if (!d->context)
        return;

    emit serviceEnabled(d->context->service()->name(), enabled);
    if (enabled &&
        d->context->service()) {
            emit serviceButtonEnabled(d->context->service()->serviceType());
            qDebug() << Q_FUNC_INFO << __LINE__ << d->context->service()->serviceType() << enabled;
    }
}

void ServiceSettingsWidget::setServiceButtonEnable(bool enable)
{
    Q_D(ServiceSettingsWidget);
    qDebug() << Q_FUNC_INFO << __LINE__;

    if (d->enableServiceButton->isChecked() != enable)
        d->enableServiceButton->setChecked(enable);
}

void ServiceSettingsWidget::openSettingsPage()
{
    Q_D(ServiceSettingsWidget);
    SettingsPage *settingsPage =
            new SettingsPage(d->context, d->enableServiceButton->model());
    settingsPage->appear();
    QObject::connect(settingsPage, SIGNAL(disappearing()),
                     this, SLOT(onSettingsPageClosed()));
}

void ServiceSettingsWidget::onSettingsPageClosed()
{
    Q_D(ServiceSettingsWidget);
    // Restore button style
    d->enableServiceButton->setStyleName("CommonLeftSwitchInverted");
}

}//end of namespace
<commit_msg>Fixing: 269013 - Google sync widget should follow the same style as MfE sync widget does<commit_after>/*
 * This file is part of accounts-ui
 *
 * Copyright (C) 2009-2010 Nokia Corporation.
 *
 * Contact: Alberto Mardegan <alberto.mardegan@nokia.com>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public License
 * version 2.1 as published by the Free Software Foundation.
 *
 * This library is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA
 */

//project
#include "service-settings-widget.h"
#include "abstract-service-setup-context.h"
#include "settings-page.h"
#include "service-helper.h"
#include "basic-service-widget.h"

//M
#include <MBasicListItem>
#include <MLayout>
#include <MLinearLayoutPolicy>
#include <MGridLayoutPolicy>
#include <MLabel>
#include <MButton>
#include <MLocale>
#include <MImageWidget>
#include <MStylableWidget>

//Qt
#include <QDebug>
#include <QDomElement>

namespace AccountsUI {

ServiceSettingsWidgetListItem::ServiceSettingsWidgetListItem(QGraphicsWidget *parent)
        : MBasicListItem(MBasicListItem::IconWithTitleAndSubtitle, parent)
{
    setStyleName("CommonLargePanelInverted");
    setObjectName("wgServiceSettingsWidgetListItem");

    horizontalLayout = new MLayout(this);

    horizontalLayout->setContentsMargins(0, 0, 0, 0);
    MLinearLayoutPolicy *horizontalLayoutPolicy
            = new MLinearLayoutPolicy(horizontalLayout, Qt::Horizontal);
    horizontalLayoutPolicy->setSpacing(0);

    MLayout *titleSubtitleLayout = new MLayout(horizontalLayout);
    titleSubtitleLayout->setContentsMargins(0, 0, 0, 0);
    MLinearLayoutPolicy *titleSubtitleLayoutPolicy = new MLinearLayoutPolicy(titleSubtitleLayout, Qt::Vertical);
    titleSubtitleLayoutPolicy->setSpacing(0);

    titleLabelWidget()->setStyleName("CommonTitleInverted");
    titleSubtitleLayoutPolicy->addItem(titleLabelWidget(), Qt::AlignLeft | Qt::AlignTop);
    subtitleLabelWidget()->setStyleName("CommonSubTitleInverted");
    titleSubtitleLayoutPolicy->addItem(subtitleLabelWidget(), Qt::AlignLeft | Qt::AlignTop);

    MStylableWidget *widget = new MStylableWidget();
    widget->setStyleName("CommonSpacer");
    titleSubtitleLayoutPolicy->addItem(widget, Qt::AlignLeft | Qt::AlignTop);

    horizontalLayoutPolicy->addItem(titleSubtitleLayout, Qt::AlignLeft | Qt::AlignCenter);

    subtitleLabelWidget()->setWordWrap(true);
    subtitleLabelWidget()->setWrapMode(QTextOption::WrapAtWordBoundaryOrAnywhere);
    subtitleLabelWidget()->setTextElide(false);

    MImageWidget *image = imageWidget();
    image->setImage("icon-m-common-drilldown-arrow-inverse");
    image->setStyleName("CommonDrillDownIcon");
    image->setObjectName("wgServiceSettingsWidgetDrillDownIcon");
    image->setVisible(false);
    horizontalLayoutPolicy->addItem(image, Qt::AlignCenter);
}

ServiceSettingsWidgetListItem::~ServiceSettingsWidgetListItem()
{}

QGraphicsLayout *ServiceSettingsWidgetListItem::createLayout()
{
    return horizontalLayout;
}

void ServiceSettingsWidgetListItem::showDrillDownButton()
{
    setStyleName("CommonLargePanelInverted");
    imageWidget()->setVisible(true);
}

class ServiceSettingsWidgetPrivate
{
public:
    ServiceSettingsWidgetPrivate()
        : context(0)
         ,enableServiceButton(0)
    {}

    ~ServiceSettingsWidgetPrivate()
    {}

    AbstractServiceSetupContext *context;
    MButton *enableServiceButton;
};

ServiceSettingsWidget::ServiceSettingsWidget(AbstractServiceSetupContext *context,
                                             QGraphicsItem *parent,
                                             int settingsConf,
                                             bool enabled)
    : MWidget(parent),
    d_ptr(new ServiceSettingsWidgetPrivate())
{
    Q_D(ServiceSettingsWidget);

    d->context = context;
    QString catalog = d->context->service()->trCatalog();
    if (!catalog.isEmpty()) {
         MLocale locale;
         locale.installTrCatalog(catalog);
         MLocale::setDefault(locale);
    }

    MLayout *containerMainLayout = new MLayout(this);
    MLinearLayoutPolicy *mainPolicy =
        new MLinearLayoutPolicy(containerMainLayout, Qt::Vertical);
    mainPolicy->setSpacing(0);
    mainPolicy->setContentsMargins(0,0,0,0);

    MWidget *upperWidget = new MWidget(this);
    MLayout *upperLayout = new MLayout(upperWidget);
    MLinearLayoutPolicy *containerMainPolicy =
        new MLinearLayoutPolicy(upperLayout, Qt::Horizontal);
    containerMainPolicy->setSpacing(0);
    containerMainPolicy->setContentsMargins(0,0,0,0);
    ServiceSettingsWidgetListItem *serviceInfoList = 0;
    BasicServiceWidget *serviceInfoLayout = 0;

    if (settingsConf & EnableButton) {
        if (context) {
            /* Dummy layout for the enable service button
             * The layout's top margin is compensating for the top
             * margin of the title label in the ServiceSettingsWidgetListItem
             * CommonTitleInverted - styling issue.
             *
             * TODO - Remove when styling will be fixed. */
            MLayout *enableServiceButtonLayout = new MLayout;
            MLinearLayoutPolicy *enableServiceButtonPolicy =
                new MLinearLayoutPolicy(enableServiceButtonLayout, Qt::Horizontal);
            enableServiceButtonPolicy->setSpacing(0);
            enableServiceButtonPolicy->setContentsMargins(0, 15, 0, 0);

            d->enableServiceButton = new MButton(this);
            d->enableServiceButton->setViewType(MButton::switchType);
            d->enableServiceButton->setStyleName("CommonLeftSwitchInverted");
            d->enableServiceButton->setObjectName("wgServiceSettingsWidgetServiceButton");
            d->enableServiceButton->setCheckable(true);
            enableServiceButtonPolicy->addItem(d->enableServiceButton);

            ServiceHelper *serviceHelper =
                new ServiceHelper(const_cast<Accounts::Service*>(context->service()), this);
            QString subTitle = serviceHelper->description();

            if (settingsConf != (MandatorySettings | EnableButton)) {
                if (!serviceHelper->shortDescription().isEmpty())
                    subTitle = serviceHelper->shortDescription();
            }

            MWidget *widget = context->widget(0);
            MWidget *serviceInfo = 0;
            if (widget) {
                serviceInfoList = new ServiceSettingsWidgetListItem();
                serviceInfoList->setTitle(serviceHelper->prettyName());
                serviceInfoList->setSubtitle(subTitle);
                serviceInfo = serviceInfoList;
            } else {
                serviceInfoLayout = new BasicServiceWidget(BasicServiceWidget::TitleAndSubtitle, this);
                serviceInfoLayout->createLayout();
                serviceInfoLayout->setTitle(serviceHelper->prettyName());
                serviceInfoLayout->setSubtitle(subTitle);
                serviceInfo = serviceInfoLayout;
            }

            /*
             * no signals during widget creation
             * */
            d->enableServiceButton->setChecked(enabled);
            connect(d->enableServiceButton, SIGNAL(toggled(bool)), this, SLOT(enabled(bool)));

            containerMainPolicy->addItem(enableServiceButtonLayout, Qt::AlignRight | Qt::AlignTop);
            containerMainPolicy->addItem(serviceInfo, Qt::AlignLeft | Qt::AlignTop);

            mainPolicy->addItem(upperWidget);
        }
    }

    if (context) {
        MWidget *widget = context->widget(0, (settingsConf & NonMandatorySettings));

        if (widget) {
            if (serviceInfoList) {
                serviceInfoList->showDrillDownButton();
                connect(serviceInfoList, SIGNAL(clicked()),
                        this, SLOT(openSettingsPage()));
            }

            if ((settingsConf & NonMandatorySettings) ||
                (settingsConf & MandatorySettings)) {
                    mainPolicy->addItem(widget);
            }
        }
    }
}

ServiceSettingsWidget::~ServiceSettingsWidget()
{
    delete d_ptr;
}

void ServiceSettingsWidget::enabled(bool enabled)
{
    Q_D(ServiceSettingsWidget);
    if (!d->context)
        return;

    emit serviceEnabled(d->context->service()->name(), enabled);
    if (enabled &&
        d->context->service()) {
            emit serviceButtonEnabled(d->context->service()->serviceType());
            qDebug() << Q_FUNC_INFO << __LINE__ << d->context->service()->serviceType() << enabled;
    }
}

void ServiceSettingsWidget::setServiceButtonEnable(bool enable)
{
    Q_D(ServiceSettingsWidget);
    qDebug() << Q_FUNC_INFO << __LINE__;

    if (d->enableServiceButton->isChecked() != enable)
        d->enableServiceButton->setChecked(enable);
}

void ServiceSettingsWidget::openSettingsPage()
{
    Q_D(ServiceSettingsWidget);
    SettingsPage *settingsPage =
            new SettingsPage(d->context, d->enableServiceButton->model());
    settingsPage->appear();
    QObject::connect(settingsPage, SIGNAL(disappearing()),
                     this, SLOT(onSettingsPageClosed()));
}

void ServiceSettingsWidget::onSettingsPageClosed()
{
    Q_D(ServiceSettingsWidget);
    // Restore button style
    d->enableServiceButton->setStyleName("CommonLeftSwitchInverted");
}

}//end of namespace
<|endoftext|>


<commit_before>//===- Steensgaard.cpp - Context Insensitive Alias Analysis ---------------===//
//
// This pass uses the data structure graphs to implement a simple context
// insensitive alias analysis.  It does this by computing the local analysis
// graphs for all of the functions, then merging them together into a single big
// graph without cloning.
//
//===----------------------------------------------------------------------===//

#include "llvm/Analysis/DataStructure.h"
#include "llvm/Analysis/DSGraph.h"
#include "llvm/Analysis/AliasAnalysis.h"
#include "llvm/Module.h"
#include "Support/Statistic.h"

namespace {
  Statistic<> NumNoAlias  ("steens", "Number of 'no alias' replies");
  Statistic<> NumMayAlias ("steens", "Number of 'may alias' replies");
};

namespace {
  class Steens : public Pass, public AliasAnalysis {
    DSGraph *ResultGraph;
  public:
    Steens() : ResultGraph(0) {}
    ~Steens() { assert(ResultGraph == 0 && "releaseMemory not called?"); }

    //------------------------------------------------
    // Implement the Pass API
    //

    // run - Build up the result graph, representing the pointer graph for the
    // program.
    //
    bool run(Module &M);

    virtual void releaseMemory() { delete ResultGraph; ResultGraph = 0; }

    virtual void getAnalysisUsage(AnalysisUsage &AU) const {
      AU.setPreservesAll();                    // Does not transform code...
      AU.addRequired<LocalDataStructures>();   // Uses local dsgraph
      AU.addRequired<AliasAnalysis>();         // Chains to another AA impl...
    }

    // print - Implement the Pass::print method...
    void print(std::ostream &O, const Module *M) const {
      assert(ResultGraph && "Result graph has not yet been computed!");
      ResultGraph->writeGraphToFile(O, "steensgaards");
    }

    //------------------------------------------------
    // Implement the AliasAnalysis API
    //

    // alias - This is the only method here that does anything interesting...
    Result alias(const Value *V1, const Value *V2);

    /// canCallModify - Not implemented yet: FIXME
    ///
    Result canCallModify(const CallInst &CI, const Value *Ptr) {
      return MayAlias;
    }

    /// canInvokeModify - Not implemented yet: FIXME
    ///
    Result canInvokeModify(const InvokeInst &I, const Value *Ptr) {
      return MayAlias;
    }

  private:
    void ResolveFunctionCall(Function *F, const DSCallSite &Call,
                             DSNodeHandle &RetVal);
  };

  // Register the pass...
  RegisterOpt<Steens> X("steens-aa",
                        "Steensgaard's FlowInsensitive/ConIns alias analysis");

  // Register as an implementation of AliasAnalysis
  RegisterAnalysisGroup<AliasAnalysis, Steens> Y;
}

/// ResolveFunctionCall - Resolve the actual arguments of a call to function F
/// with the specified call site descriptor.  This function links the arguments
/// and the return value for the call site context-insensitively.
///
void Steens::ResolveFunctionCall(Function *F,
                                 const DSCallSite &Call,
                                 DSNodeHandle &RetVal) {
  assert(ResultGraph != 0 && "Result graph not allocated!");
  hash_map<Value*, DSNodeHandle> &ValMap = ResultGraph->getScalarMap();

  // Handle the return value of the function...
  if (Call.getRetVal().getNode() && RetVal.getNode())
    RetVal.mergeWith(Call.getRetVal());

  // Loop over all pointer arguments, resolving them to their provided pointers
  unsigned PtrArgIdx = 0;
  for (Function::aiterator AI = F->abegin(), AE = F->aend(); AI != AE; ++AI) {
    hash_map<Value*, DSNodeHandle>::iterator I = ValMap.find(AI);
    if (I != ValMap.end())    // If its a pointer argument...
      I->second.addEdgeTo(Call.getPtrArg(PtrArgIdx++));
  }

  assert(PtrArgIdx == Call.getNumPtrArgs() && "Argument resolution mismatch!");
}

/// run - Build up the result graph, representing the pointer graph for the
/// program.
///
bool Steens::run(Module &M) {
  assert(ResultGraph == 0 && "Result graph already allocated!");
  LocalDataStructures &LDS = getAnalysis<LocalDataStructures>();

  // Create a new, empty, graph...
  ResultGraph = new DSGraph();

  // RetValMap - Keep track of the return values for all functions that return
  // valid pointers.
  //
  hash_map<Function*, DSNodeHandle> RetValMap;

  // Loop over the rest of the module, merging graphs for non-external functions
  // into this graph.
  //
  for (Module::iterator I = M.begin(), E = M.end(); I != E; ++I)
    if (!I->isExternal()) {
      hash_map<Value*, DSNodeHandle> ValMap;
      {  // Scope to free NodeMap memory ASAP
        hash_map<const DSNode*, DSNodeHandle> NodeMap;
        const DSGraph &FDSG = LDS.getDSGraph(*I);
        DSNodeHandle RetNode = ResultGraph->cloneInto(FDSG, ValMap, NodeMap);

        // Keep track of the return node of the function's graph if it returns a
        // value...
        //
        if (RetNode.getNode())
          RetValMap[I] = RetNode;
      }

      // Incorporate the inlined Function's ScalarMap into the global
      // ScalarMap...
      hash_map<Value*, DSNodeHandle> &GVM = ResultGraph->getScalarMap();

      while (!ValMap.empty()) { // Loop over value map, moving entries over...
        const std::pair<Value*, DSNodeHandle> &DSN = *ValMap.begin();
        hash_map<Value*, DSNodeHandle>::iterator I = GVM.find(DSN.first);
        if (I == GVM.end())
          GVM[DSN.first] = DSN.second;
        else
          I->second.mergeWith(DSN.second);
        ValMap.erase(ValMap.begin());
      }
    }

  // FIXME: Must recalculate and use the Incomplete markers!!

  // Now that we have all of the graphs inlined, we can go about eliminating
  // call nodes...
  //
  std::vector<DSCallSite> &Calls =
    ResultGraph->getAuxFunctionCalls();
  assert(Calls.empty() && "Aux call list is already in use??");

  // Start with a copy of the original call sites...
  Calls = ResultGraph->getFunctionCalls();

  for (unsigned i = 0; i != Calls.size(); ) {
    DSCallSite &CurCall = Calls[i];

    // Loop over the called functions, eliminating as many as possible...
    std::vector<GlobalValue*> CallTargets =
      CurCall.getCallee().getNode()->getGlobals();
    for (unsigned c = 0; c != CallTargets.size(); ) {
      // If we can eliminate this function call, do so!
      bool Eliminated = false;
      if (Function *F = dyn_cast<Function>(CallTargets[c]))
        if (!F->isExternal()) {
          ResolveFunctionCall(F, CurCall, RetValMap[F]);
          Eliminated = true;
        }
      if (Eliminated)
        CallTargets.erase(CallTargets.begin()+c);
      else
        ++c;  // Cannot eliminate this call, skip over it...
    }

    if (CallTargets.empty())          // Eliminated all calls?
      Calls.erase(Calls.begin()+i);   // Remove from call list...
    else
      ++i;                            // Skip this call site...
  }

  // Update the "incomplete" markers on the nodes, ignoring unknownness due to
  // incoming arguments...
  ResultGraph->maskIncompleteMarkers();
  ResultGraph->markIncompleteNodes(DSGraph::IgnoreFormalArgs);

  // Remove any nodes that are dead after all of the merging we have done...
  ResultGraph->removeDeadNodes(DSGraph::KeepUnreachableGlobals);

  DEBUG(print(std::cerr, &M));
  return false;
}

// alias - This is the only method here that does anything interesting...
AliasAnalysis::Result Steens::alias(const Value *V1, const Value *V2) {
  assert(ResultGraph && "Result graph has not been computed yet!");

  hash_map<Value*, DSNodeHandle> &GVM = ResultGraph->getScalarMap();

  hash_map<Value*, DSNodeHandle>::iterator I = GVM.find(const_cast<Value*>(V1));
  if (I != GVM.end() && I->second.getNode()) {
    DSNodeHandle &V1H = I->second;
    hash_map<Value*, DSNodeHandle>::iterator J=GVM.find(const_cast<Value*>(V2));
    if (J != GVM.end() && J->second.getNode()) {
      DSNodeHandle &V2H = J->second;
      // If the two pointers point to different data structure graph nodes, they
      // cannot alias!
      if (V1H.getNode() != V2H.getNode()) {
        ++NumNoAlias;
        return NoAlias;
      }
      // FIXME: If the two pointers point to the same node, and the offsets are
      // different, and the LinkIndex vector doesn't alias the section, then the
      // two pointers do not alias.  We need access size information for the two
      // accesses though!
      //
    }
  }

  // Since Steensgaard cannot do any better, count it as a 'may alias'
  ++NumMayAlias;

  // If we cannot determine alias properties based on our graph, fall back on
  // some other AA implementation.
  //
  return getAnalysis<AliasAnalysis>().alias(V1, V2);
}
<commit_msg>Rename variable<commit_after>//===- Steensgaard.cpp - Context Insensitive Alias Analysis ---------------===//
//
// This pass uses the data structure graphs to implement a simple context
// insensitive alias analysis.  It does this by computing the local analysis
// graphs for all of the functions, then merging them together into a single big
// graph without cloning.
//
//===----------------------------------------------------------------------===//

#include "llvm/Analysis/DataStructure.h"
#include "llvm/Analysis/DSGraph.h"
#include "llvm/Analysis/AliasAnalysis.h"
#include "llvm/Module.h"
#include "Support/Statistic.h"

namespace {
  Statistic<> NumNoAlias  ("steens", "Number of 'no alias' replies");
  Statistic<> NumMayAlias ("steens", "Number of 'may alias' replies");
};

namespace {
  class Steens : public Pass, public AliasAnalysis {
    DSGraph *ResultGraph;
  public:
    Steens() : ResultGraph(0) {}
    ~Steens() { assert(ResultGraph == 0 && "releaseMemory not called?"); }

    //------------------------------------------------
    // Implement the Pass API
    //

    // run - Build up the result graph, representing the pointer graph for the
    // program.
    //
    bool run(Module &M);

    virtual void releaseMemory() { delete ResultGraph; ResultGraph = 0; }

    virtual void getAnalysisUsage(AnalysisUsage &AU) const {
      AU.setPreservesAll();                    // Does not transform code...
      AU.addRequired<LocalDataStructures>();   // Uses local dsgraph
      AU.addRequired<AliasAnalysis>();         // Chains to another AA impl...
    }

    // print - Implement the Pass::print method...
    void print(std::ostream &O, const Module *M) const {
      assert(ResultGraph && "Result graph has not yet been computed!");
      ResultGraph->writeGraphToFile(O, "steensgaards");
    }

    //------------------------------------------------
    // Implement the AliasAnalysis API
    //

    // alias - This is the only method here that does anything interesting...
    Result alias(const Value *V1, const Value *V2);

    /// canCallModify - Not implemented yet: FIXME
    ///
    Result canCallModify(const CallInst &CI, const Value *Ptr) {
      return MayAlias;
    }

    /// canInvokeModify - Not implemented yet: FIXME
    ///
    Result canInvokeModify(const InvokeInst &I, const Value *Ptr) {
      return MayAlias;
    }

  private:
    void ResolveFunctionCall(Function *F, const DSCallSite &Call,
                             DSNodeHandle &RetVal);
  };

  // Register the pass...
  RegisterOpt<Steens> X("steens-aa",
                        "Steensgaard's FlowInsensitive/ConIns alias analysis");

  // Register as an implementation of AliasAnalysis
  RegisterAnalysisGroup<AliasAnalysis, Steens> Y;
}

/// ResolveFunctionCall - Resolve the actual arguments of a call to function F
/// with the specified call site descriptor.  This function links the arguments
/// and the return value for the call site context-insensitively.
///
void Steens::ResolveFunctionCall(Function *F,
                                 const DSCallSite &Call,
                                 DSNodeHandle &RetVal) {
  assert(ResultGraph != 0 && "Result graph not allocated!");
  hash_map<Value*, DSNodeHandle> &ValMap = ResultGraph->getScalarMap();

  // Handle the return value of the function...
  if (Call.getRetVal().getNode() && RetVal.getNode())
    RetVal.mergeWith(Call.getRetVal());

  // Loop over all pointer arguments, resolving them to their provided pointers
  unsigned PtrArgIdx = 0;
  for (Function::aiterator AI = F->abegin(), AE = F->aend(); AI != AE; ++AI) {
    hash_map<Value*, DSNodeHandle>::iterator I = ValMap.find(AI);
    if (I != ValMap.end())    // If its a pointer argument...
      I->second.addEdgeTo(Call.getPtrArg(PtrArgIdx++));
  }

  assert(PtrArgIdx == Call.getNumPtrArgs() && "Argument resolution mismatch!");
}

/// run - Build up the result graph, representing the pointer graph for the
/// program.
///
bool Steens::run(Module &M) {
  assert(ResultGraph == 0 && "Result graph already allocated!");
  LocalDataStructures &LDS = getAnalysis<LocalDataStructures>();

  // Create a new, empty, graph...
  ResultGraph = new DSGraph();

  // RetValMap - Keep track of the return values for all functions that return
  // valid pointers.
  //
  hash_map<Function*, DSNodeHandle> RetValMap;

  // Loop over the rest of the module, merging graphs for non-external functions
  // into this graph.
  //
  for (Module::iterator I = M.begin(), E = M.end(); I != E; ++I)
    if (!I->isExternal()) {
      hash_map<Value*, DSNodeHandle> ValMap;
      {  // Scope to free NodeMap memory ASAP
        hash_map<const DSNode*, DSNodeHandle> NodeMap;
        const DSGraph &FDSG = LDS.getDSGraph(*I);
        DSNodeHandle RetNode = ResultGraph->cloneInto(FDSG, ValMap, NodeMap);

        // Keep track of the return node of the function's graph if it returns a
        // value...
        //
        if (RetNode.getNode())
          RetValMap[I] = RetNode;
      }

      // Incorporate the inlined Function's ScalarMap into the global
      // ScalarMap...
      hash_map<Value*, DSNodeHandle> &GVM = ResultGraph->getScalarMap();

      while (!ValMap.empty()) { // Loop over value map, moving entries over...
        const std::pair<Value*, DSNodeHandle> &DSN = *ValMap.begin();
        hash_map<Value*, DSNodeHandle>::iterator I = GVM.find(DSN.first);
        if (I == GVM.end())
          GVM[DSN.first] = DSN.second;
        else
          I->second.mergeWith(DSN.second);
        ValMap.erase(ValMap.begin());
      }
    }

  // FIXME: Must recalculate and use the Incomplete markers!!

  // Now that we have all of the graphs inlined, we can go about eliminating
  // call nodes...
  //
  std::vector<DSCallSite> &Calls =
    ResultGraph->getAuxFunctionCalls();
  assert(Calls.empty() && "Aux call list is already in use??");

  // Start with a copy of the original call sites...
  Calls = ResultGraph->getFunctionCalls();

  for (unsigned i = 0; i != Calls.size(); ) {
    DSCallSite &CurCall = Calls[i];

    // Loop over the called functions, eliminating as many as possible...
    std::vector<GlobalValue*> CallTargets =
      CurCall.getCallee().getNode()->getGlobals();
    for (unsigned c = 0; c != CallTargets.size(); ) {
      // If we can eliminate this function call, do so!
      bool Eliminated = false;
      if (Function *F = dyn_cast<Function>(CallTargets[c]))
        if (!F->isExternal()) {
          ResolveFunctionCall(F, CurCall, RetValMap[F]);
          Eliminated = true;
        }
      if (Eliminated)
        CallTargets.erase(CallTargets.begin()+c);
      else
        ++c;  // Cannot eliminate this call, skip over it...
    }

    if (CallTargets.empty())          // Eliminated all calls?
      Calls.erase(Calls.begin()+i);   // Remove from call list...
    else
      ++i;                            // Skip this call site...
  }

  // Update the "incomplete" markers on the nodes, ignoring unknownness due to
  // incoming arguments...
  ResultGraph->maskIncompleteMarkers();
  ResultGraph->markIncompleteNodes(DSGraph::IgnoreFormalArgs);

  // Remove any nodes that are dead after all of the merging we have done...
  ResultGraph->removeDeadNodes(DSGraph::KeepUnreachableGlobals);

  DEBUG(print(std::cerr, &M));
  return false;
}

// alias - This is the only method here that does anything interesting...
AliasAnalysis::Result Steens::alias(const Value *V1, const Value *V2) {
  assert(ResultGraph && "Result graph has not been computed yet!");

  hash_map<Value*, DSNodeHandle> &GSM = ResultGraph->getScalarMap();

  hash_map<Value*, DSNodeHandle>::iterator I = GSM.find(const_cast<Value*>(V1));
  if (I != GSM.end() && I->second.getNode()) {
    DSNodeHandle &V1H = I->second;
    hash_map<Value*, DSNodeHandle>::iterator J=GSM.find(const_cast<Value*>(V2));
    if (J != GSM.end() && J->second.getNode()) {
      DSNodeHandle &V2H = J->second;
      // If the two pointers point to different data structure graph nodes, they
      // cannot alias!
      if (V1H.getNode() != V2H.getNode()) {  // FIXME: Handle incompleteness!
        ++NumNoAlias;
        return NoAlias;
      }
      // FIXME: If the two pointers point to the same node, and the offsets are
      // different, and the LinkIndex vector doesn't alias the section, then the
      // two pointers do not alias.  We need access size information for the two
      // accesses though!
      //
    }
  }

  // Since Steensgaard cannot do any better, count it as a 'may alias'
  ++NumMayAlias;

  // If we cannot determine alias properties based on our graph, fall back on
  // some other AA implementation.
  //
  return getAnalysis<AliasAnalysis>().alias(V1, V2);
}
<|endoftext|>