<commit_before>#include "Derivative.h"
#include "IRMutator.h"
#include "Substitute.h"
#include "Scope.h"
#include "IROperator.h"

namespace Halide {
namespace Internal {

using std::string;

class FiniteDifference : public IRMutator {
    Scope<Expr> scope;
    string var;

    Expr brute_force(Expr e) {
        return substitute(var, (Variable::make(Int(32), var)) + 1, e) - e;
    }

    using IRMutator::visit;

    void visit(const IntImm *) {
        expr = 0;
    }

    void visit(const FloatImm *) {
        expr = 0.0f;
    }

    void visit(const Cast *op) {
        expr = brute_force(op);
    }

    void visit(const Variable *op) {
        if (op->name == var) {
            expr = make_one(op->type);
        } else if (scope.contains(op->name)) {
            expr = scope.get(op->name);
        } else {
            expr = make_zero(op->type);
        }
    }

    void visit(const Add *op) {
        expr = mutate(op->a) + mutate(op->b);
    }

    void visit(const Sub *op) {
        expr = mutate(op->a) - mutate(op->b);
    }

    void visit(const Mul *op) {
        Expr da = mutate(op->a), db = mutate(op->b);
        expr = op->a * db + da * op->b + da * db;
    }

    void visit(const Div *op) {
        expr = brute_force(op);
    }

    void visit(const Mod *op) {
        expr = brute_force(op);
    }

    void visit(const Min *op) {
        expr = select(op->a < op->b, mutate(op->a), mutate(op->b));
    }

    void visit(const Max *op) {
        expr = select(op->a > op->b, mutate(op->a), mutate(op->b));
    }

    void visit(const Select *op) {
        expr = select(op->condition, mutate(op->true_value), mutate(op->false_value));
    }

    void visit(const Load *op) {
        expr = brute_force(op);
    }

    void visit(const Call *op) {
        expr = brute_force(op);
    }

    void visit(const Let *op) {
        scope.push(op->name, mutate(op->value));
        expr = mutate(op->body);
        scope.pop(op->name);
    }
public:
    FiniteDifference(string v) : var(v) {}
};

Expr finite_difference(Expr expr, const string &var) {
    return FiniteDifference(var).mutate(expr);
}

class Monotonic : public IRVisitor {
    const string &var;

    Scope<MonotonicResult> scope;

    void visit(const IntImm *) {
        result = Constant;
    }

    void visit(const FloatImm *) {
        result = Constant;
    }

    void visit(const StringImm *) {
        assert(false && "Monotonic on String");
    }

    void visit(const Cast *op) {
        // It's possible to reason about this, but for now we punt.
        op->value.accept(this);
        if (result != Constant) {
            result = Unknown;
        }
    }

    void visit(const Variable *op) {
        if (op->name == var) {
            result = MonotonicIncreasing;
        } else if (scope.contains(op->name)) {
            result = scope.get(op->name);
        } else {
            result = Constant;
        }
    }

    MonotonicResult flip(MonotonicResult r) {
        switch (r) {
        case MonotonicIncreasing: return MonotonicDecreasing;
        case MonotonicDecreasing: return MonotonicIncreasing;
        default: return r;
        }
    }

    MonotonicResult unify(MonotonicResult a, MonotonicResult b) {
        if (a == b) {
            return a;
        }

        if (a == Unknown || b == Unknown) {
            return Unknown;
        }

        if (a == Constant) {
            return b;
        }

        if (b == Constant) {
            return a;
        }

        return Unknown;
    }

    void visit(const Add *op) {
        op->a.accept(this);
        MonotonicResult ra = result;
        op->b.accept(this);
        MonotonicResult rb = result;
        result = unify(ra, rb);
    }

    void visit(const Sub *op) {
        op->a.accept(this);
        MonotonicResult ra = result;
        op->b.accept(this);
        MonotonicResult rb = result;
        result = unify(ra, flip(rb));
    }

    void visit(const Mul *op) {
        op->a.accept(this);
        MonotonicResult ra = result;
        op->b.accept(this);
        MonotonicResult rb = result;
        result = unify(ra, rb);
    }

    void visit(const Div *op) {
        op->a.accept(this);
        MonotonicResult ra = result;
        op->b.accept(this);
        MonotonicResult rb = result;
        result = unify(ra, flip(rb));
    }

    void visit(const Mod *op) {
        result = Unknown;
    }

    void visit(const Min *op) {
        op->a.accept(this);
        MonotonicResult ra = result;
        op->b.accept(this);
        MonotonicResult rb = result;
        result = unify(ra, rb);
    }

    void visit(const Max *op) {
        op->a.accept(this);
        MonotonicResult ra = result;
        op->b.accept(this);
        MonotonicResult rb = result;
        result = unify(ra, rb);
    }

    void visit(const EQ *op) {
        assert(false && "Monotonic of bool");
    }

    void visit(const NE *op) {
        assert(false && "Monotonic of bool");
    }

    void visit(const LT *op) {
        assert(false && "Monotonic of bool");
    }

    void visit(const LE *op) {
        assert(false && "Monotonic of bool");
    }

    void visit(const GT *op) {
        assert(false && "Monotonic of bool");
    }

    void visit(const GE *op) {
        assert(false && "Monotonic of bool");
    }

    void visit(const And *op) {
        assert(false && "Monotonic of bool");
    }

    void visit(const Or *op) {
        assert(false && "Monotonic of bool");
    }

    void visit(const Not *op) {
        assert(false && "Monotonic of bool");
    }

    void visit(const Select *op) {
        // TODO: This is a bug! What if the condition depends on the var.
        op->true_value.accept(this);
        MonotonicResult ra = result;
        op->false_value.accept(this);
        MonotonicResult rb = result;
        result = unify(ra, rb);
    }

    void visit(const Load *op) {
        op->index.accept(this);
        if (result != Constant) {
            result = Unknown;
        }
    }

    void visit(const Ramp *op) {
        assert(false && "Monotonic of vector");
    }

    void visit(const Broadcast *op) {
        assert(false && "Monotonic of vector");
    }

    void visit(const Call *op) {
        for (size_t i = 0; i < op->args.size(); i++) {
            op->args[i].accept(this);
            if (result != Constant) {
                result = Unknown;
                return;
            }
        }
        result = Constant;
    }

    void visit(const Let *op) {
        op->value.accept(this);

        if (result == Constant) {
            // No point pushing it if it's constant w.r.t the var,
            // because unknown variables are treated as constant.
            op->body.accept(this);
        } else {
            scope.push(op->name, result);
            op->body.accept(this);
            scope.pop(op->name);
        }
    }

    void visit(const LetStmt *op) {
        assert(false && "Monotonic of statement");
    }

    void visit(const AssertStmt *op) {
        assert(false && "Monotonic of statement");
    }

    void visit(const Pipeline *op) {
        assert(false && "Monotonic of statement");
    }

    void visit(const For *op) {
        assert(false && "Monotonic of statement");
    }

    void visit(const Store *op) {
        assert(false && "Monotonic of statement");
    }

    void visit(const Provide *op) {
        assert(false && "Monotonic of statement");
    }

    void visit(const Allocate *op) {
        assert(false && "Monotonic of statement");
    }

    void visit(const Free *op) {
        assert(false && "Monotonic of statement");
    }

    void visit(const Realize *op) {
        assert(false && "Monotonic of statement");
    }

    void visit(const Block *op) {
        assert(false && "Monotonic of statement");
    }

    void visit(const IfThenElse *op) {
        assert(false && "Monotonic of statement");
    }

    void visit(const Evaluate *op) {
        assert(false && "Monotonic of statement");
    }
public:
    MonotonicResult result;

    Monotonic(const std::string &v) : var(v), result(Unknown) {}
};

MonotonicResult is_monotonic(Expr e, const std::string &var) {
    if (!e.defined()) return Unknown;
    Monotonic m(var);
    e.accept(&m);
    return m.result;
}

}
}
<commit_msg>Bugfix for is_monotonic of a select<commit_after>#include "Derivative.h"
#include "IRMutator.h"
#include "Substitute.h"
#include "Scope.h"
#include "IROperator.h"

namespace Halide {
namespace Internal {

using std::string;

class FiniteDifference : public IRMutator {
    Scope<Expr> scope;
    string var;

    Expr brute_force(Expr e) {
        return substitute(var, (Variable::make(Int(32), var)) + 1, e) - e;
    }

    using IRMutator::visit;

    void visit(const IntImm *) {
        expr = 0;
    }

    void visit(const FloatImm *) {
        expr = 0.0f;
    }

    void visit(const Cast *op) {
        expr = brute_force(op);
    }

    void visit(const Variable *op) {
        if (op->name == var) {
            expr = make_one(op->type);
        } else if (scope.contains(op->name)) {
            expr = scope.get(op->name);
        } else {
            expr = make_zero(op->type);
        }
    }

    void visit(const Add *op) {
        expr = mutate(op->a) + mutate(op->b);
    }

    void visit(const Sub *op) {
        expr = mutate(op->a) - mutate(op->b);
    }

    void visit(const Mul *op) {
        Expr da = mutate(op->a), db = mutate(op->b);
        expr = op->a * db + da * op->b + da * db;
    }

    void visit(const Div *op) {
        expr = brute_force(op);
    }

    void visit(const Mod *op) {
        expr = brute_force(op);
    }

    void visit(const Min *op) {
        expr = select(op->a < op->b, mutate(op->a), mutate(op->b));
    }

    void visit(const Max *op) {
        expr = select(op->a > op->b, mutate(op->a), mutate(op->b));
    }

    void visit(const Select *op) {
        expr = select(op->condition, mutate(op->true_value), mutate(op->false_value));
    }

    void visit(const Load *op) {
        expr = brute_force(op);
    }

    void visit(const Call *op) {
        expr = brute_force(op);
    }

    void visit(const Let *op) {
        scope.push(op->name, mutate(op->value));
        expr = mutate(op->body);
        scope.pop(op->name);
    }
public:
    FiniteDifference(string v) : var(v) {}
};

Expr finite_difference(Expr expr, const string &var) {
    return FiniteDifference(var).mutate(expr);
}

class Monotonic : public IRVisitor {
    const string &var;

    Scope<MonotonicResult> scope;

    void visit(const IntImm *) {
        result = Constant;
    }

    void visit(const FloatImm *) {
        result = Constant;
    }

    void visit(const StringImm *) {
        assert(false && "Monotonic on String");
    }

    void visit(const Cast *op) {
        // It's possible to reason about this, but for now we punt.
        op->value.accept(this);
        if (result != Constant) {
            result = Unknown;
        }
    }

    void visit(const Variable *op) {
        if (op->name == var) {
            result = MonotonicIncreasing;
        } else if (scope.contains(op->name)) {
            result = scope.get(op->name);
        } else {
            result = Constant;
        }
    }

    MonotonicResult flip(MonotonicResult r) {
        switch (r) {
        case MonotonicIncreasing: return MonotonicDecreasing;
        case MonotonicDecreasing: return MonotonicIncreasing;
        default: return r;
        }
    }

    MonotonicResult unify(MonotonicResult a, MonotonicResult b) {
        if (a == b) {
            return a;
        }

        if (a == Unknown || b == Unknown) {
            return Unknown;
        }

        if (a == Constant) {
            return b;
        }

        if (b == Constant) {
            return a;
        }

        return Unknown;
    }

    void visit(const Add *op) {
        op->a.accept(this);
        MonotonicResult ra = result;
        op->b.accept(this);
        MonotonicResult rb = result;
        result = unify(ra, rb);
    }

    void visit(const Sub *op) {
        op->a.accept(this);
        MonotonicResult ra = result;
        op->b.accept(this);
        MonotonicResult rb = result;
        result = unify(ra, flip(rb));
    }

    void visit(const Mul *op) {
        op->a.accept(this);
        MonotonicResult ra = result;
        op->b.accept(this);
        MonotonicResult rb = result;
        result = unify(ra, rb);
    }

    void visit(const Div *op) {
        op->a.accept(this);
        MonotonicResult ra = result;
        op->b.accept(this);
        MonotonicResult rb = result;
        result = unify(ra, flip(rb));
    }

    void visit(const Mod *op) {
        result = Unknown;
    }

    void visit(const Min *op) {
        op->a.accept(this);
        MonotonicResult ra = result;
        op->b.accept(this);
        MonotonicResult rb = result;
        result = unify(ra, rb);
    }

    void visit(const Max *op) {
        op->a.accept(this);
        MonotonicResult ra = result;
        op->b.accept(this);
        MonotonicResult rb = result;
        result = unify(ra, rb);
    }

    void visit_cmp(Expr a, Expr b) {
        a.accept(this);
        MonotonicResult ra = result;
        b.accept(this);
        MonotonicResult rb = result;
        if (ra == Constant && rb == Constant) {
            result = Constant;
        } else {
            result = Unknown;
        }
    }

    void visit(const EQ *op) {
        visit_cmp(op->a, op->b);
    }

    void visit(const NE *op) {
        visit_cmp(op->a, op->b);
    }

    void visit(const LT *op) {
        visit_cmp(op->a, op->b);
    }

    void visit(const LE *op) {
        visit_cmp(op->a, op->b);
    }

    void visit(const GT *op) {
        visit_cmp(op->a, op->b);
    }

    void visit(const GE *op) {
        visit_cmp(op->a, op->b);
    }

    void visit(const And *op) {
        visit_cmp(op->a, op->b);
    }

    void visit(const Or *op) {
        visit_cmp(op->a, op->b);
    }

    void visit(const Not *op) {
        op->a.accept(this);
    }

    void visit(const Select *op) {
        op->condition.accept(this);
        MonotonicResult rcond = result;

        if (rcond != Constant) {
            result = Unknown;
        } else {
            op->true_value.accept(this);
            MonotonicResult ra = result;
            op->false_value.accept(this);
            MonotonicResult rb = result;
            result = unify(ra, rb);
        }
    }

    void visit(const Load *op) {
        op->index.accept(this);
        if (result != Constant) {
            result = Unknown;
        }
    }

    void visit(const Ramp *op) {
        assert(false && "Monotonic of vector");
    }

    void visit(const Broadcast *op) {
        assert(false && "Monotonic of vector");
    }

    void visit(const Call *op) {
        for (size_t i = 0; i < op->args.size(); i++) {
            op->args[i].accept(this);
            if (result != Constant) {
                result = Unknown;
                return;
            }
        }
        result = Constant;
    }

    void visit(const Let *op) {
        op->value.accept(this);

        if (result == Constant) {
            // No point pushing it if it's constant w.r.t the var,
            // because unknown variables are treated as constant.
            op->body.accept(this);
        } else {
            scope.push(op->name, result);
            op->body.accept(this);
            scope.pop(op->name);
        }
    }

    void visit(const LetStmt *op) {
        assert(false && "Monotonic of statement");
    }

    void visit(const AssertStmt *op) {
        assert(false && "Monotonic of statement");
    }

    void visit(const Pipeline *op) {
        assert(false && "Monotonic of statement");
    }

    void visit(const For *op) {
        assert(false && "Monotonic of statement");
    }

    void visit(const Store *op) {
        assert(false && "Monotonic of statement");
    }

    void visit(const Provide *op) {
        assert(false && "Monotonic of statement");
    }

    void visit(const Allocate *op) {
        assert(false && "Monotonic of statement");
    }

    void visit(const Free *op) {
        assert(false && "Monotonic of statement");
    }

    void visit(const Realize *op) {
        assert(false && "Monotonic of statement");
    }

    void visit(const Block *op) {
        assert(false && "Monotonic of statement");
    }

    void visit(const IfThenElse *op) {
        assert(false && "Monotonic of statement");
    }

    void visit(const Evaluate *op) {
        assert(false && "Monotonic of statement");
    }
public:
    MonotonicResult result;

    Monotonic(const std::string &v) : var(v), result(Unknown) {}
};

MonotonicResult is_monotonic(Expr e, const std::string &var) {
    if (!e.defined()) return Unknown;
    Monotonic m(var);
    e.accept(&m);
    return m.result;
}

}
}
<|endoftext|>


<commit_before>#include "FileSystem.hpp"
#include "File.hpp"
#include <iostream>
#include <cmath>

using namespace std;

FileSystem::FileSystem() {
    root = new Directory("/");
	mMemblockDevice = MemBlockDevice(4, 4);
	freeBlockNumbers = vector<bool>(mMemblockDevice.size(), true);
}

FileSystem::~FileSystem() {
    delete root;
}

void FileSystem::ls(const std::string &path) const {
    Directory* directory = root->getDirectory(path);
    if (directory == nullptr) {
        cout << "Directory does not exist." << endl;
        return;
    }

    directory->ls();
}

void FileSystem::format() {
	mMemblockDevice.reset();
	freeBlockNumbers = vector<bool>(mMemblockDevice.size(), true);
    delete root;
    root = new Directory("/");
}

void FileSystem::save(const std::string &saveFile) const{
	std::ofstream file;
	int nrOfBlocks = mMemblockDevice.size();
	int nrOfElements = mMemblockDevice.getBlockLength();
	char* buffer = new char[nrOfElements];
	file.open(saveFile, std::ios::out|std::ios::binary);

	for (int i = 0; i < nrOfBlocks; i++){
		for (int j = 0; j < nrOfElements; j++){
			buffer[j] = mMemblockDevice.readBlock(i)[j];
		}
		file.write(buffer, nrOfElements);
	}
	file.close();
}

void FileSystem::load(const std::string &saveFile) {
	std::ifstream file;
	int nrOfBlocks = mMemblockDevice.size();
	int nrOfElements = mMemblockDevice.getBlockLength();
	char* buffer = new char[nrOfElements];
	file.open(saveFile, std::ios::in | std::ios::binary);

	for (int i = 0; i < nrOfBlocks; i++){
		file.read(buffer, nrOfElements);
		mMemblockDevice.writeBlock(i, buffer);
	}
	file.close();
}

void FileSystem::create(const std::string &filePath){
	if (fileOrDirectoryExists(filePath))
		return;

    Directory* directory = root->getDirectory(directoryPart(filePath));
	File* file = directory->createFile(filePart(filePath));

	cout << "Enter file contents: \n";
	string fileContent;
    getline(cin, fileContent);
	appendToFile(file, fileContent);
}

void FileSystem::append(const std::string &source, const std::string &app){
	Directory* directory = root->getDirectory(directoryPart(source));
	File* file = directory->getFile(filePart(source));
	if (fileExists(source) && fileExists(app)){
		string appendString = fileToString(app);
		appendToFile(file, appendString);
	}
}

string FileSystem::fileToString(const std::string &path) const{
	Directory* directory = root->getDirectory(directoryPart(path));
	File* file = directory->getFile(filePart(path));
	vector<int> tempNrs = file->getBlockNumbers();
	string contents;
	char* buffer= new char[mMemblockDevice.getBlockLength()];
	int bufferPos = 0;
	//Read blocks and print characters until there is no more characters left or we have read a whole block (and should start to read a new block).
	for (int i = 0; i < tempNrs.size(); i++){
		while ((bufferPos < (mMemblockDevice.getBlockLength()-1)) && (mMemblockDevice.readBlock(tempNrs[i])[bufferPos] != '\0')){
			contents += mMemblockDevice.readBlock(tempNrs[i])[bufferPos];
			bufferPos++;
		}
		bufferPos = 0;
	}
	return contents;
}

void FileSystem::appendToFile(File* file, string contents){
	vector<int> freeBlock = freeBlocks();
	vector<int> usedBlockNumbers;
	int bufferPos = 0;		//Keeps track of where we are in the buffer
	int freeBlockPos = 0;	//Keeps track of what free block number we are using
	int requiredBlocks = 0;	//Keeps track of how many blocks we need to allocate
	char* buffer = new char[mMemblockDevice.getBlockLength()];		//Stores what we want to write to the block

	if (file->getBlockNumbers().size() > 0){
		//The file we are appending to is not a new file.
		//Recalculate file length
		int tempLength = file->getLength() + contents.length();
		//Calculate how many new blocks we need, if any.
		//If four total blocks required and 2 already allocated, required blocks will be 2.
		requiredBlocks = ceil(tempLength / (float)mMemblockDevice.getBlockLength()) - file->getBlockNumbers().size();
		if (requiredBlocks > freeBlock.size()){
			cout << "Not enough free blocks.\n";
			return;
		}
		file->setLength(tempLength);

		//Null out buffer.
		for (int i = 0; i < mMemblockDevice.getBlockLength(); i++)
			buffer[i] = '\0';
		//read the last block of the file into the buffer
		char c;
		while ((bufferPos < ((float)mMemblockDevice.getBlockLength())) && ((c = mMemblockDevice.readBlock(file->getBlockNumbers().back())[bufferPos]) != '\0')){
			buffer[bufferPos] = c;
			bufferPos++;
		}
		usedBlockNumbers = file->getBlockNumbers();
	} else {
		requiredBlocks = ceil(contents.length() / (float)mMemblockDevice.getBlockLength());
		//Check to see if there are enough free blocks.
		if (requiredBlocks > freeBlock.size()){
			cout << "Not enough free blocks.\n";
			return;
		}
		file->setLength(contents.length());
		for (int i = 0; i < mMemblockDevice.getBlockLength(); i++)
			buffer[i] = '\0';
		bufferPos = 0;

	}

	int test = contents.length();
	for (int j = 0; j < contents.length(); j++){
		buffer[bufferPos] = contents[j];
		bufferPos++;
		if (j == (contents.length()-1)){//If we have reached the end of the string and should write the contents to block.
			//If the files blocknumbers size is greater than 0, then the file already existed and we should append the buffer contents to the last block in the file
			if (file->getBlockNumbers().size() > 0){
				mMemblockDevice.writeBlock(file->getBlockNumbers().back(), buffer);
			}
			else { //if the file didn't exist previously, we should allocate new blocks to write to.
				mMemblockDevice.writeBlock(freeBlock[freeBlockPos], buffer);
				freeBlockNumbers[freeBlock[freeBlockPos]] = false;
				usedBlockNumbers.push_back(freeBlock[freeBlockPos]);
			}
		} else if (bufferPos >(mMemblockDevice.getBlockLength())) { //If pos is greater than block length, we need to write to next block.
			mMemblockDevice.writeBlock(freeBlock[freeBlockPos], buffer);
			freeBlockNumbers[freeBlock[freeBlockPos]] = false;
			usedBlockNumbers.push_back(freeBlock[freeBlockPos]);
			//Null out buffer.
			for (int i = 0; i < mMemblockDevice.getBlockLength(); i++)
				buffer[i] = '\0';
			bufferPos = 0;
			freeBlockPos++;
		}
	}
	file->setBlockNumbers(usedBlockNumbers);
}

void FileSystem::chmod(const std::string &path, int permission){
	Directory* directory = root->getDirectory(directoryPart(path));
	File* file = directory->getFile(filePart(path));
	if (permission > 4 || permission < 0){
		cout << "Invalid permission level. Valid permission levels:\n";
		cout << "0 = NONE.\n";
		cout << "1 = READ only.\n";
		cout << "2 = WRITE only.\n";
		cout << "3 = READ and WRITE permission.\n";
	}
	file->setPermission(Permission(permission));
}

vector<int> FileSystem::freeBlocks() const{
	vector<int> blockList;
	for (int i = 0; i < freeBlockNumbers.size(); i++){
		if (freeBlockNumbers[i] == true)
			blockList.push_back(i);
	}
	return blockList;
}

void FileSystem::mkdir(const string &path) {
    if (filePart(path).empty()) {
        cout << "Wrong syntax." << endl;
        return;
    }

    Directory* directory = root->getDirectory(directoryPart(path));
    if (directory == nullptr) {
        cout << "Directory " << directoryPart(path) << " does not exist." << endl;
        return;
    }

	if (fileOrDirectoryExists(path))
		return;

    directory->createDirectory(filePart(path));

    cout << "Directory created." << endl;
}

void FileSystem::cat(const std::string &fileName) const{
    Directory* directory = root->getDirectory(directoryPart(fileName));
    if (directory == nullptr) {
        cout << "File does not exist." << endl;
        return;
    }

    File* file = directory->getFile(filePart(fileName));
	if (file == nullptr){
        cout << "File does not exist." << endl;
        return;
	}
	else if (!file->getReadPermission()){
		cout << "File is read protected." << endl;
		return;
	}

	vector<int> tempNrs = file->getBlockNumbers();
	char* buffer = new char[mMemblockDevice.getBlockLength()];
	int bufferPos = 0;
	//Read blocks and print characters until there is no more characters left or we have read a whole block (and should start to read a new block).
	for (int i = 0; i < tempNrs.size(); i++){
		while ( (bufferPos < (mMemblockDevice.getBlockLength())) && (mMemblockDevice.readBlock(tempNrs[i])[bufferPos] != '\0') ){
			cout << mMemblockDevice.readBlock(tempNrs[i])[bufferPos];
			bufferPos++;
		}
		bufferPos = 0;
	}
	cout << '\n';
}

void FileSystem::copy(const std::string &source, const std::string &dest){
	if (!fileExists(source)){
		cout << "Can't copy file, file doest not exist.";
		return;
	}
	if (fileExists(dest)){
		cout << "Can't copy file, destination already exists.";
	}
	File* file = root->createFile(filePart(dest));
	string contents = fileToString(source);
	appendToFile(file, contents);
}

void FileSystem::rm(const std::string &path){
	if (!fileExists(path))
		return;
	Directory* directory = root->getDirectory(directoryPart(path));
	File* file = directory->getFile(filePart(path));

	if (!file->getWritePermission()){
		cout << "File is write protected." << endl;
		return;
	}

	for (int i = 0; i < file->getBlockNumbers().size(); i++){
		freeBlockNumbers[file->getBlockNumbers()[i]] = true;
	}
	delete file;
	directory->rm(filePart(path));
}

bool FileSystem::directoryExists(const string &path) {
    return root->getDirectory(path) != nullptr;
}

bool FileSystem::fileOrDirectoryExists(const string &path) const{
	Directory* directory = root->getDirectory(directoryPart(path));
	if (directory->getDirectory(filePart(path)) != nullptr) {
		cout << "Directory with that name already exists." << endl;
		return true;
	}

	if (directory->getFile(filePart(path)) != nullptr) {
		cout << "File with same name already exists." << endl;
		return true;
	}
	return false;
}

bool FileSystem::fileExists(const string &path) const{
	Directory* directory = root->getDirectory(directoryPart(path));
	if (directory->getFile(filePart(path)) != nullptr) {
		return true;
	}
	return false;
}

string FileSystem::directoryPart(const string &path) {
    size_t pos = path.find_last_of('/');
    if (pos == string::npos)
        return "";

    return path.substr(0, pos);
}

string FileSystem::filePart(const string &path) {
    size_t pos = path.find_last_of('/');
    if (pos == string::npos)
        return path;

    if (path.length() < pos + 2)
        return "";

    return path.substr(pos + 1);
}
<commit_msg>appendToFile utterly fixed<commit_after>#include "FileSystem.hpp"
#include "File.hpp"
#include <iostream>
#include <cmath>

using namespace std;

FileSystem::FileSystem() {
    root = new Directory("/");
	mMemblockDevice = MemBlockDevice(4, 4);
	freeBlockNumbers = vector<bool>(mMemblockDevice.size(), true);
}

FileSystem::~FileSystem() {
    delete root;
}

void FileSystem::ls(const std::string &path) const {
    Directory* directory = root->getDirectory(path);
    if (directory == nullptr) {
        cout << "Directory does not exist." << endl;
        return;
    }

    directory->ls();
}

void FileSystem::format() {
	mMemblockDevice.reset();
	freeBlockNumbers = vector<bool>(mMemblockDevice.size(), true);
    delete root;
    root = new Directory("/");
}

void FileSystem::save(const std::string &saveFile) const{
	std::ofstream file;
	int nrOfBlocks = mMemblockDevice.size();
	int nrOfElements = mMemblockDevice.getBlockLength();
	char* buffer = new char[nrOfElements];
	file.open(saveFile, std::ios::out|std::ios::binary);

	for (int i = 0; i < nrOfBlocks; i++){
		for (int j = 0; j < nrOfElements; j++){
			buffer[j] = mMemblockDevice.readBlock(i)[j];
		}
		file.write(buffer, nrOfElements);
	}
	file.close();
}

void FileSystem::load(const std::string &saveFile) {
	std::ifstream file;
	int nrOfBlocks = mMemblockDevice.size();
	int nrOfElements = mMemblockDevice.getBlockLength();
	char* buffer = new char[nrOfElements];
	file.open(saveFile, std::ios::in | std::ios::binary);

	for (int i = 0; i < nrOfBlocks; i++){
		file.read(buffer, nrOfElements);
		mMemblockDevice.writeBlock(i, buffer);
	}
	file.close();
}

void FileSystem::create(const std::string &filePath){
	if (fileOrDirectoryExists(filePath))
		return;
	cout << "Enter file contents: \n";
	string fileContent;
	getline(cin, fileContent);

	int requiredBlocks = ceil(fileContent.length() / (float)mMemblockDevice.getBlockLength());
	vector<int> freeBlock = freeBlocks();
	if (requiredBlocks > freeBlock.size()){
		cout << "Not enough free blocks to save string." << endl;
		return;
	}

    Directory* directory = root->getDirectory(directoryPart(filePath));
	File* file = directory->createFile(filePart(filePath));

	appendToFile(file, fileContent);
}

void FileSystem::append(const std::string &source, const std::string &app){
	Directory* directory = root->getDirectory(directoryPart(source));
	File* file = directory->getFile(filePart(source));
	if (fileExists(source) && fileExists(app)){
		string appendString = fileToString(app);
		appendToFile(file, appendString);
	}
}

string FileSystem::fileToString(const std::string &path) const{
	Directory* directory = root->getDirectory(directoryPart(path));
	File* file = directory->getFile(filePart(path));
	vector<int> tempNrs = file->getBlockNumbers();
	string contents;
	char* buffer= new char[mMemblockDevice.getBlockLength()];
	int bufferPos = 0;
	//Read blocks and print characters until there is no more characters left or we have read a whole block (and should start to read a new block).
	for (int i = 0; i < tempNrs.size(); i++){
		while ((bufferPos < (mMemblockDevice.getBlockLength()-1)) && (mMemblockDevice.readBlock(tempNrs[i])[bufferPos] != '\0')){
			contents += mMemblockDevice.readBlock(tempNrs[i])[bufferPos];
			bufferPos++;
		}
		bufferPos = 0;
	}
	return contents;
}

void FileSystem::appendToFile(File* file, string contents){
	vector<int> freeBlock = freeBlocks();
	vector<int> usedBlockNumbers;
	int bufferPos = 0;												//Keeps track of where we are in the buffer
	int freeBlockPos = 0;											//Keeps track of what free block number we are using
	int requiredBlocks = 0;											//Keeps track of how many blocks we may need to allocate
	char* buffer = new char[mMemblockDevice.getBlockLength()];		//Stores what we want to write to the block

	if (file->getBlockNumbers().size() > 0){
		//The file we are appending to is not a new file.
		//Recalculate file length
		int tempLength = file->getLength() + contents.length();
		//Calculate how many new blocks we need, if any.
		requiredBlocks = ceil(tempLength / (float)mMemblockDevice.getBlockLength()) - file->getBlockNumbers().size();
		if (requiredBlocks > freeBlock.size()){
			cout << "Not enough free blocks.\n";
			return;
		}
		file->setLength(tempLength);

		//Null out buffer.
		for (int i = 0; i < mMemblockDevice.getBlockLength(); i++)
			buffer[i] = '\0';
		//read the last block of the file into the buffer
		char c;
		while ((bufferPos < ((float)mMemblockDevice.getBlockLength())) && ((c = mMemblockDevice.readBlock(file->getBlockNumbers().back())[bufferPos]) != '\0')){
			buffer[bufferPos] = c;
			bufferPos++;
		}
		usedBlockNumbers = file->getBlockNumbers();
		usedBlockNumbers.pop_back();
		//Insert the files last block number at the start of freeBlock,
		//indicating that if we need a new block then we should write the buffer
		//to the files last block.
		freeBlock.insert(freeBlock.begin(), file->getBlockNumbers().back());
	} else {
		requiredBlocks = ceil(contents.length() / (float)mMemblockDevice.getBlockLength());
		file->setLength(contents.length());
		for (int i = 0; i < mMemblockDevice.getBlockLength(); i++)
			buffer[i] = '\0';
		bufferPos = 0;

	}

	int test = contents.length();
	for (int j = 0; j < contents.length(); j++){
		buffer[bufferPos] = contents[j];
		bufferPos++;
		if (j == (contents.length()-1)){//If we have reached the end of the string and should write the contents to block.
			mMemblockDevice.writeBlock(freeBlock[freeBlockPos], buffer);
			freeBlockNumbers[freeBlock[freeBlockPos]] = false;
			usedBlockNumbers.push_back(freeBlock[freeBlockPos]);
		} else if (bufferPos == (mMemblockDevice.getBlockLength())) { //If pos is greater than block length, we need to write to next block.
			mMemblockDevice.writeBlock(freeBlock[freeBlockPos], buffer);
			freeBlockNumbers[freeBlock[freeBlockPos]] = false;
			usedBlockNumbers.push_back(freeBlock[freeBlockPos]);
			//Null out buffer.
			for (int i = 0; i < mMemblockDevice.getBlockLength(); i++)
				buffer[i] = '\0';
			bufferPos = 0;
			freeBlockPos++;
		}
	}
	freeBlocks();
	file->setBlockNumbers(usedBlockNumbers);
}

void FileSystem::chmod(const std::string &path, int permission){
	Directory* directory = root->getDirectory(directoryPart(path));
	File* file = directory->getFile(filePart(path));
	if (permission > 4 || permission < 0){
		cout << "Invalid permission level. Valid permission levels:\n";
		cout << "0 = NONE.\n";
		cout << "1 = READ only.\n";
		cout << "2 = WRITE only.\n";
		cout << "3 = READ and WRITE permission.\n";
	}
	file->setPermission(Permission(permission));
}

vector<int> FileSystem::freeBlocks() const{
	vector<int> blockList;
	for (int i = 0; i < freeBlockNumbers.size(); i++){
		if (freeBlockNumbers[i] == true)
			blockList.push_back(i);
	}
	return blockList;
}

void FileSystem::mkdir(const string &path) {
    if (filePart(path).empty()) {
        cout << "Wrong syntax." << endl;
        return;
    }

    Directory* directory = root->getDirectory(directoryPart(path));
    if (directory == nullptr) {
        cout << "Directory " << directoryPart(path) << " does not exist." << endl;
        return;
    }

	if (fileOrDirectoryExists(path))
		return;

    directory->createDirectory(filePart(path));

    cout << "Directory created." << endl;
}

void FileSystem::cat(const std::string &fileName) const{
    Directory* directory = root->getDirectory(directoryPart(fileName));
    if (directory == nullptr) {
        cout << "File does not exist." << endl;
        return;
    }

    File* file = directory->getFile(filePart(fileName));
	if (file == nullptr){
        cout << "File does not exist." << endl;
        return;
	}
	else if (!file->getReadPermission()){
		cout << "File is read protected." << endl;
		return;
	}

	vector<int> tempNrs = file->getBlockNumbers();
	char* buffer = new char[mMemblockDevice.getBlockLength()];
	int bufferPos = 0;
	//Read blocks and print characters until there is no more characters left or we have read a whole block (and should start to read a new block).
	for (int i = 0; i < tempNrs.size(); i++){
		while ( (bufferPos < (mMemblockDevice.getBlockLength())) && (mMemblockDevice.readBlock(tempNrs[i])[bufferPos] != '\0') ){
			cout << mMemblockDevice.readBlock(tempNrs[i])[bufferPos];
			bufferPos++;
		}
		bufferPos = 0;
	}
	cout << '\n';
}

void FileSystem::copy(const std::string &source, const std::string &dest){
	if (!fileExists(source)){
		cout << "Can't copy file, file doest not exist.";
		return;
	}
	if (fileExists(dest)){
		cout << "Can't copy file, destination already exists.";
	}
	File* file = root->createFile(filePart(dest));
	string contents = fileToString(source);
	appendToFile(file, contents);
}

void FileSystem::rm(const std::string &path){
	if (!fileExists(path))
		return;
	Directory* directory = root->getDirectory(directoryPart(path));
	File* file = directory->getFile(filePart(path));

	if (!file->getWritePermission()){
		cout << "File is write protected." << endl;
		return;
	}

	for (int i = 0; i < file->getBlockNumbers().size(); i++){
		freeBlockNumbers[file->getBlockNumbers()[i]] = true;
	}
	delete file;
	directory->rm(filePart(path));
}

bool FileSystem::directoryExists(const string &path) {
    return root->getDirectory(path) != nullptr;
}

bool FileSystem::fileOrDirectoryExists(const string &path) const{
	Directory* directory = root->getDirectory(directoryPart(path));
	if (directory->getDirectory(filePart(path)) != nullptr) {
		cout << "Directory with that name already exists." << endl;
		return true;
	}

	if (directory->getFile(filePart(path)) != nullptr) {
		cout << "File with same name already exists." << endl;
		return true;
	}
	return false;
}

bool FileSystem::fileExists(const string &path) const{
	Directory* directory = root->getDirectory(directoryPart(path));
	if (directory->getFile(filePart(path)) != nullptr) {
		return true;
	}
	return false;
}

string FileSystem::directoryPart(const string &path) {
    size_t pos = path.find_last_of('/');
    if (pos == string::npos)
        return "";

    return path.substr(0, pos);
}

string FileSystem::filePart(const string &path) {
    size_t pos = path.find_last_of('/');
    if (pos == string::npos)
        return path;

    if (path.length() < pos + 2)
        return "";

    return path.substr(pos + 1);
}
<|endoftext|>


<commit_before>/*
    This file is part of KOrganizer.

    Copyright (c) 2001,2002,2003 Cornelius Schumacher <schumacher@kde.org>

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

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

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.

    As a special exception, permission is given to link this program
    with any edition of Qt, and distribute the resulting executable,
    without including the source code for Qt in the source distribution.
*/

#include <kdebug.h>
#include <klocale.h>

#include "koglobals.h"

#include <kcalendarsystem.h>

#include "datenavigatorcontainer.h"

DateNavigatorContainer::DateNavigatorContainer( QWidget *parent,
                                                const char *name )
  : QWidget( parent, name ), mCalendar( 0 ),
    mHorizontalCount( 1 ), mVerticalCount( 1 )
{
  mExtraViews.setAutoDelete( true );

  mNavigatorView = new KDateNavigator( this, name );

  connectNavigatorView( mNavigatorView );
}

DateNavigatorContainer::~DateNavigatorContainer()
{
}

void DateNavigatorContainer::connectNavigatorView( KDateNavigator *v )
{
  connect( v, SIGNAL( datesSelected( const KCal::DateList & ) ),
           SIGNAL( datesSelected( const KCal::DateList & ) ) );
  connect( v, SIGNAL( incidenceDropped( Incidence * ) ),
           SIGNAL( incidenceDropped( Incidence * ) ) );
  connect( v, SIGNAL( incidenceDroppedMove( Incidence *, Incidence * ) ),
           SIGNAL( incidenceDroppedMove( Incidence *, Incidence * ) ) );
  connect( v, SIGNAL( weekClicked( const QDate & ) ),
           SIGNAL( weekClicked( const QDate & ) ) );

  connect( v, SIGNAL( goPrevious() ), SIGNAL( goPrevious() ) );
  connect( v, SIGNAL( goNext() ), SIGNAL( goNext() ) );

  connect( v, SIGNAL( goNextMonth() ), SIGNAL( goNextMonth() ) );
  connect( v, SIGNAL( goPrevMonth() ), SIGNAL( goPrevMonth() ) );
  connect( v, SIGNAL( goNextYear() ), SIGNAL( goNextYear() ) );
  connect( v, SIGNAL( goPrevYear() ), SIGNAL( goPrevYear() ) );

  connect( v, SIGNAL( goMonth( int ) ), SIGNAL( goMonth( int ) ) );
}

void DateNavigatorContainer::setCalendar( Calendar *cal )
{
  mCalendar = cal;
  mNavigatorView->setCalendar( cal );
  KDateNavigator *n;
  for( n = mExtraViews.first(); n; n = mExtraViews.next() ) {
    n->setCalendar( cal );
  }
}

void DateNavigatorContainer::updateDayMatrix()
{
  mNavigatorView->updateDayMatrix();
  KDateNavigator *n;
  for( n = mExtraViews.first(); n; n = mExtraViews.next() ) {
    n->updateDayMatrix();
  }
}

void DateNavigatorContainer::updateToday()
{
  mNavigatorView->updateToday();
  KDateNavigator *n;
  for( n = mExtraViews.first(); n; n = mExtraViews.next() ) {
    n->updateToday();
  }
}

void DateNavigatorContainer::updateView()
{
  mNavigatorView->updateView();
  KDateNavigator *n;
  for( n = mExtraViews.first(); n; n = mExtraViews.next() ) {
    n->updateView();
  }
}

void DateNavigatorContainer::updateConfig()
{
  mNavigatorView->updateConfig();
  KDateNavigator *n;
  for( n = mExtraViews.first(); n; n = mExtraViews.next() ) {
    n->updateConfig();
  }
}

void DateNavigatorContainer::selectDates( const DateList &dateList )
{
  mNavigatorView->selectDates( dateList );
  setBaseDates();
}

void DateNavigatorContainer::setBaseDates()
{
  KCal::DateList dateList = mNavigatorView->selectedDates();
  if ( dateList.isEmpty() ) {
    kdError() << "DateNavigatorContainer::selectDates() empty list." << endl;
  }
  QDate baseDate = dateList.first();
  KDateNavigator *n;
  for( n = mExtraViews.first(); n; n = mExtraViews.next() ) {
    baseDate = KOGlobals::self()->calendarSystem()->addMonths( baseDate, 1 );
    n->setBaseDate( baseDate );
  }
}

void DateNavigatorContainer::resizeEvent( QResizeEvent * )
{
#if 0
  kdDebug() << "DateNavigatorContainer::resizeEvent()" << endl;
  kdDebug() << "  CURRENT SIZE: " << size() << endl;
  kdDebug() << "  MINIMUM SIZEHINT: " << minimumSizeHint() << endl;
  kdDebug() << "  SIZEHINT: " << sizeHint() << endl;
  kdDebug() << "  MINIMUM SIZE: " << minimumSize() << endl;
#endif

  QSize minSize = mNavigatorView->minimumSizeHint();

//  kdDebug() << "  NAVIGATORVIEW minimumSizeHint: " << minSize << endl;

  int verticalCount = size().height() / minSize.height();
  int horizontalCount = size().width() / minSize.width();

  if ( horizontalCount != mHorizontalCount ||
       verticalCount != mVerticalCount ) {
    uint count = horizontalCount * verticalCount;

    while ( count > ( mExtraViews.count() + 1 ) ) {
      KDateNavigator *n = new KDateNavigator( this );
      mExtraViews.append( n );
      n->setCalendar( mCalendar );
      setBaseDates();
      connectNavigatorView( n );
      n->show();
    }

    while ( count < ( mExtraViews.count() + 1 ) ) {
      mExtraViews.removeLast();
    }

    mHorizontalCount = horizontalCount;
    mVerticalCount = verticalCount;
  }

  int height = size().height() / verticalCount;
  int width = size().width() / horizontalCount;

  mNavigatorView->setGeometry( 0, 0, width, height );
  for( uint i = 0; i < mExtraViews.count(); ++i ) {
    int x = ( i + 1 ) % horizontalCount;
    int y = ( i + 1 ) / horizontalCount;

    mExtraViews.at( i )->setGeometry( x * width, y * height, width, height );
  }
}

QSize DateNavigatorContainer::minimumSizeHint() const
{
  return mNavigatorView->minimumSizeHint();
}

QSize DateNavigatorContainer::sizeHint() const
{
  return mNavigatorView->sizeHint();
}

#include "datenavigatorcontainer.moc"
<commit_msg>Don't endlessly try to layout zero widgets.<commit_after>/*
    This file is part of KOrganizer.

    Copyright (c) 2001,2002,2003 Cornelius Schumacher <schumacher@kde.org>

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

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

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.

    As a special exception, permission is given to link this program
    with any edition of Qt, and distribute the resulting executable,
    without including the source code for Qt in the source distribution.
*/

#include <kdebug.h>
#include <klocale.h>

#include "koglobals.h"

#include <kcalendarsystem.h>

#include "datenavigatorcontainer.h"

DateNavigatorContainer::DateNavigatorContainer( QWidget *parent,
                                                const char *name )
  : QWidget( parent, name ), mCalendar( 0 ),
    mHorizontalCount( 1 ), mVerticalCount( 1 )
{
  mExtraViews.setAutoDelete( true );

  mNavigatorView = new KDateNavigator( this, name );

  connectNavigatorView( mNavigatorView );
}

DateNavigatorContainer::~DateNavigatorContainer()
{
}

void DateNavigatorContainer::connectNavigatorView( KDateNavigator *v )
{
  connect( v, SIGNAL( datesSelected( const KCal::DateList & ) ),
           SIGNAL( datesSelected( const KCal::DateList & ) ) );
  connect( v, SIGNAL( incidenceDropped( Incidence * ) ),
           SIGNAL( incidenceDropped( Incidence * ) ) );
  connect( v, SIGNAL( incidenceDroppedMove( Incidence *, Incidence * ) ),
           SIGNAL( incidenceDroppedMove( Incidence *, Incidence * ) ) );
  connect( v, SIGNAL( weekClicked( const QDate & ) ),
           SIGNAL( weekClicked( const QDate & ) ) );

  connect( v, SIGNAL( goPrevious() ), SIGNAL( goPrevious() ) );
  connect( v, SIGNAL( goNext() ), SIGNAL( goNext() ) );

  connect( v, SIGNAL( goNextMonth() ), SIGNAL( goNextMonth() ) );
  connect( v, SIGNAL( goPrevMonth() ), SIGNAL( goPrevMonth() ) );
  connect( v, SIGNAL( goNextYear() ), SIGNAL( goNextYear() ) );
  connect( v, SIGNAL( goPrevYear() ), SIGNAL( goPrevYear() ) );

  connect( v, SIGNAL( goMonth( int ) ), SIGNAL( goMonth( int ) ) );
}

void DateNavigatorContainer::setCalendar( Calendar *cal )
{
  mCalendar = cal;
  mNavigatorView->setCalendar( cal );
  KDateNavigator *n;
  for( n = mExtraViews.first(); n; n = mExtraViews.next() ) {
    n->setCalendar( cal );
  }
}

void DateNavigatorContainer::updateDayMatrix()
{
  mNavigatorView->updateDayMatrix();
  KDateNavigator *n;
  for( n = mExtraViews.first(); n; n = mExtraViews.next() ) {
    n->updateDayMatrix();
  }
}

void DateNavigatorContainer::updateToday()
{
  mNavigatorView->updateToday();
  KDateNavigator *n;
  for( n = mExtraViews.first(); n; n = mExtraViews.next() ) {
    n->updateToday();
  }
}

void DateNavigatorContainer::updateView()
{
  mNavigatorView->updateView();
  KDateNavigator *n;
  for( n = mExtraViews.first(); n; n = mExtraViews.next() ) {
    n->updateView();
  }
}

void DateNavigatorContainer::updateConfig()
{
  mNavigatorView->updateConfig();
  KDateNavigator *n;
  for( n = mExtraViews.first(); n; n = mExtraViews.next() ) {
    n->updateConfig();
  }
}

void DateNavigatorContainer::selectDates( const DateList &dateList )
{
  mNavigatorView->selectDates( dateList );
  setBaseDates();
}

void DateNavigatorContainer::setBaseDates()
{
  KCal::DateList dateList = mNavigatorView->selectedDates();
  if ( dateList.isEmpty() ) {
    kdError() << "DateNavigatorContainer::selectDates() empty list." << endl;
  }
  QDate baseDate = dateList.first();
  KDateNavigator *n;
  for( n = mExtraViews.first(); n; n = mExtraViews.next() ) {
    baseDate = KOGlobals::self()->calendarSystem()->addMonths( baseDate, 1 );
    n->setBaseDate( baseDate );
  }
}

void DateNavigatorContainer::resizeEvent( QResizeEvent * )
{
#if 0
  kdDebug() << "DateNavigatorContainer::resizeEvent()" << endl;
  kdDebug() << "  CURRENT SIZE: " << size() << endl;
  kdDebug() << "  MINIMUM SIZEHINT: " << minimumSizeHint() << endl;
  kdDebug() << "  SIZEHINT: " << sizeHint() << endl;
  kdDebug() << "  MINIMUM SIZE: " << minimumSize() << endl;
#endif

  QSize minSize = mNavigatorView->minimumSizeHint();

//  kdDebug() << "  NAVIGATORVIEW minimumSizeHint: " << minSize << endl;

  int verticalCount = size().height() / minSize.height();
  int horizontalCount = size().width() / minSize.width();

  if ( horizontalCount != mHorizontalCount ||
       verticalCount != mVerticalCount ) {
    uint count = horizontalCount * verticalCount;
    if ( count == 0 ) return;

    while ( count > ( mExtraViews.count() + 1 ) ) {
      KDateNavigator *n = new KDateNavigator( this );
      mExtraViews.append( n );
      n->setCalendar( mCalendar );
      setBaseDates();
      connectNavigatorView( n );
      n->show();
    }

    while ( count < ( mExtraViews.count() + 1 ) ) {
      mExtraViews.removeLast();
    }

    mHorizontalCount = horizontalCount;
    mVerticalCount = verticalCount;
  }

  int height = size().height() / verticalCount;
  int width = size().width() / horizontalCount;

  mNavigatorView->setGeometry( 0, 0, width, height );
  for( uint i = 0; i < mExtraViews.count(); ++i ) {
    int x = ( i + 1 ) % horizontalCount;
    int y = ( i + 1 ) / horizontalCount;

    mExtraViews.at( i )->setGeometry( x * width, y * height, width, height );
  }
}

QSize DateNavigatorContainer::minimumSizeHint() const
{
  return mNavigatorView->minimumSizeHint();
}

QSize DateNavigatorContainer::sizeHint() const
{
  return mNavigatorView->sizeHint();
}

#include "datenavigatorcontainer.moc"
<|endoftext|>


<commit_before>/*
Copyright (c), Helios
All rights reserved.

Distributed under a permissive license. See COPYING.txt for details.
*/

#include "stdafx.h"
#include "HashFilter.h"
#include "Utility.h"

size_t HashCalculator::get_hash_length() const{
	return this->hash->DigestSize();
}

void HashCalculator::get_result(void *buffer, size_t max_length){
	if (max_length >= this->get_hash_length()){
		this->hash->Final((byte *)buffer);
		return;
	}
	quick_buffer b(this->get_hash_length());
	this->hash->Final((byte *)b.data);
	memcpy(buffer, b.data, max_length);
}

std::streamsize HashOutputFilter::write(write_callback_t cb, void *ud, const void *input, std::streamsize length){
	this->update(input, length);
	std::streamsize ret = 0;
	while (length){
		auto temp = cb(ud, input, length);
		ret += temp;
		input = (const char *)input + temp;
		length -= temp;
	}
	return ret;
}

bool HashOutputFilter::flush(write_callback_t cb, void *ud){
	return true;
}

std::streamsize HashInputFilter::read(read_callback_t cb, void *ud, void *output, std::streamsize length){
	auto head = output;
	std::streamsize ret = 0;
	bool eof = false;
	while (length){
		auto temp = cb(ud, output, length);
		if (temp < 0){
			eof = true;
			break;
		}
		ret += temp;
		output = (char *)output + temp;
		length -= temp;
	}
	this->update(output, ret);
	return !ret && eof ? -1 : ret;
}
<commit_msg>Fixed bug in hash filter.<commit_after>/*
Copyright (c), Helios
All rights reserved.

Distributed under a permissive license. See COPYING.txt for details.
*/

#include "stdafx.h"
#include "HashFilter.h"
#include "Utility.h"

size_t HashCalculator::get_hash_length() const{
	return this->hash->DigestSize();
}

void HashCalculator::get_result(void *buffer, size_t max_length){
	if (max_length >= this->get_hash_length()){
		this->hash->Final((byte *)buffer);
		return;
	}
	quick_buffer b(this->get_hash_length());
	this->hash->Final((byte *)b.data);
	memcpy(buffer, b.data, max_length);
}

std::streamsize HashOutputFilter::write(write_callback_t cb, void *ud, const void *input, std::streamsize length){
	this->update(input, length);
	std::streamsize ret = 0;
	while (length){
		auto temp = cb(ud, input, length);
		ret += temp;
		input = (const char *)input + temp;
		length -= temp;
	}
	return ret;
}

bool HashOutputFilter::flush(write_callback_t cb, void *ud){
	return true;
}

std::streamsize HashInputFilter::read(read_callback_t cb, void *ud, void *output, std::streamsize length){
	auto head = output;
	std::streamsize ret = 0;
	bool eof = false;
	while (length){
		auto temp = cb(ud, output, length);
		if (temp < 0){
			eof = true;
			break;
		}
		ret += temp;
		output = (char *)output + temp;
		length -= temp;
	}
	this->update(head, ret);
	return !ret && eof ? -1 : ret;
}
<|endoftext|>


<commit_before>// This file was developed by Thomas Müller <thomas94@gmx.net>.
// It is published under the BSD 3-Clause License within the LICENSE file.

#include <tev/HelpWindow.h>

#include <nanogui/button.h>
#include <nanogui/entypo.h>
#include <nanogui/label.h>
#include <nanogui/layout.h>
#include <nanogui/opengl.h>
#include <nanogui/tabwidget.h>
#include <nanogui/window.h>

using namespace nanogui;
using namespace std;

TEV_NAMESPACE_BEGIN

#ifdef __APPLE__
string HelpWindow::COMMAND = "Cmd";
#else
string HelpWindow::COMMAND = "Ctrl";
#endif

#ifdef __APPLE__
string HelpWindow::ALT = "Opt";
#else
string HelpWindow::ALT = "Alt";
#endif

HelpWindow::HelpWindow(Widget *parent, function<void()> closeCallback)
    : Window{parent, "Help"}, mCloseCallback{closeCallback} {

    auto closeButton = new Button{buttonPanel(), "", ENTYPO_ICON_CROSS};
    closeButton->setCallback(mCloseCallback);

    setLayout(new GroupLayout{});
    setFixedWidth(600);

    TabWidget* tabWidget = new TabWidget{this};

    // Keybindings tab
    {
        Widget* shortcuts = tabWidget->createTab("Keybindings");
        shortcuts->setLayout(new GroupLayout{});

        auto addRow = [](Widget* current, string keys, string desc) {
            auto row = new Widget{current};
            row->setLayout(new BoxLayout{Orientation::Horizontal, Alignment::Fill, 0, 10});
            auto descWidget = new Label{row, desc, "sans"};
            descWidget->setFixedWidth(250);
            new Label{row, keys, "sans-bold"};
        };

        auto addSpacer = [](Widget* current) {
            auto row = new Widget{current};
            row->setHeight(10);
        };

        new Label{shortcuts, "Image Loading", "sans-bold", 18};
        auto imageLoading = new Widget{shortcuts};
        imageLoading->setLayout(new BoxLayout{Orientation::Vertical, Alignment::Fill, 0, 0});

        addRow(imageLoading, COMMAND + "+O",                             "Open Image");
        addRow(imageLoading, COMMAND + "+S",                             "Save View as Image");
        addRow(imageLoading, COMMAND + "+R or F5",                       "Reload Image");
        addRow(imageLoading, COMMAND + "+Shift+R or " + COMMAND + "+F5", "Reload All Images");
        addRow(imageLoading, COMMAND + "+W",                             "Close Image");

        new Label{shortcuts, "Image Options", "sans-bold", 18};
        auto imageSelection = new Widget{shortcuts};
        imageSelection->setLayout(new BoxLayout{Orientation::Vertical, Alignment::Fill, 0, 0});

        addRow(imageSelection, "Left Click",          "Select Hovered Image");
        addRow(imageSelection, "1…9",                 "Select N-th Image");
        addRow(imageSelection, "Down or S / Up or W", "Select Next Image");

        addSpacer(imageSelection);

        addRow(imageSelection, "F", "Fit Image to Screen");
        addRow(imageSelection, "N", "Normalize Image to [0, 1]");
        addRow(imageSelection, "R", "Reset Image Parameters");

        addSpacer(imageSelection);

        addRow(imageSelection, "Shift+Right or Shift+D / Shift+Left or Shift+A", "Select Next / Previous Tonemap");

        addSpacer(imageSelection);

        addRow(imageSelection, "E / Shift+E", "Increase / Decrease Exposure by 0.5");
        addRow(imageSelection, "O / Shift+O", "Increase / Decrease Offset by 0.1");

        new Label{shortcuts, "Reference Options", "sans-bold", 18};
        auto referenceSelection = new Widget{shortcuts};
        referenceSelection->setLayout(new BoxLayout{Orientation::Vertical, Alignment::Fill, 0, 0});

        addRow(referenceSelection, "Shift+Left Click or Right Click",             "Select Hovered Image as Reference");
        addRow(referenceSelection, "Shift+1…9",                                   "Select N-th Image as Reference");
        addRow(referenceSelection, "Shift+Down or Shift+S / Shift+Up or Shift+W", "Select Next / Previous Image as Reference");

        addSpacer(referenceSelection);

        addRow(referenceSelection, "Ctrl+Right or Ctrl+D / Ctrl+Left or Ctrl+A", "Select Next / Previous Error Metric");

        new Label{shortcuts, "Layer Options", "sans-bold", 18};
        auto layerSelection = new Widget{shortcuts};
        layerSelection->setLayout(new BoxLayout{Orientation::Vertical, Alignment::Fill, 0, 0});

        addRow(layerSelection, "Left Click",             "Select Hovered Layer");
        addRow(layerSelection, "Ctrl+1…9",               "Select N-th Layer");
        addRow(layerSelection, "Right or D / Left or A", "Select Next / Previous Layer");

        new Label{shortcuts, "Interface", "sans-bold", 18};
        auto ui = new Widget{shortcuts};
        ui->setLayout(new BoxLayout{Orientation::Vertical, Alignment::Fill, 0, 0});

        addRow(ui, ALT + "+Enter", "Maximize");
        addRow(ui, COMMAND + "+B", "Toggle GUI");
        addRow(ui, "H",            "Show Help (this Window)");
        addRow(ui, COMMAND + "+P", "Find Image or Layer");
        addRow(ui, "Q or Esc",     "Quit");
    }

    // About tab
    {
        Widget* about = tabWidget->createTab("About");
        about->setLayout(new GroupLayout{});

        auto addText = [](Widget* current, string text, string font = "sans", int fontSize = 18) {
            auto row = new Widget{current};
            row->setLayout(new BoxLayout{Orientation::Vertical, Alignment::Middle, 0, 10});
            new Label{row, text, font, fontSize };
        };

        auto addLibrary = [](Widget* current, string name, string license, string desc) {
            auto row = new Widget{current};
            row->setLayout(new BoxLayout{Orientation::Horizontal, Alignment::Fill, 5, 30});
            auto leftColumn = new Widget{row};
            leftColumn->setLayout(new BoxLayout{Orientation::Vertical, Alignment::Maximum});
            leftColumn->setFixedWidth(130);

            new Label{leftColumn, name, "sans-bold", 18};
            new Label{row, desc, "sans", 18};
        };

        auto addSpacer = [](Widget* current, int space) {
            auto row = new Widget{current};
            row->setHeight(space);
        };

        addSpacer(about, 15);

        addText(about, "tev — The EXR Viewer", "sans-bold", 46);
        addText(about, "version " TEV_VERSION, "sans", 26);

        addSpacer(about, 60);

        addText(about, "tev was developed by Thomas Müller and is released under the BSD 3-Clause License.");
        addText(about, "It was built directly or indirectly upon the following amazing third-party libraries.");

        addSpacer(about, 40);

        addLibrary(about, "args",              "", "Single-Header Argument Parsing Library.");
        addLibrary(about, "Eigen",             "", "C++ Template Library for Linear Algebra.");
        addLibrary(about, "Glad",              "", "Multi-Language GL Loader-Generator.");
        addLibrary(about, "GLEW",              "", "The OpenGL Extension Wrangler Library.");
        addLibrary(about, "GLFW",              "", "OpenGL Desktop Development Library.");
        addLibrary(about, "NanoGUI",           "", "Small Widget Library for OpenGL.");
        addLibrary(about, "NanoVG",            "", "Small Vector Graphics Library.");
        addLibrary(about, "OpenEXR",           "", "High Dynamic-Range (HDR) Image File Format.");
        addLibrary(about, "stb_image(_write)", "", "Single-Header Library for Loading and Writing Images.");
        addLibrary(about, "tinyformat",        "", "Minimal Type-Safe printf() Replacement.");
    }

    tabWidget->setActiveTab(0);
}

bool HelpWindow::keyboardEvent(int key, int scancode, int action, int modifiers) {
    if (Window::keyboardEvent(key, scancode, action, modifiers)) {
        return true;
    }

    if (key == GLFW_KEY_ESCAPE) {
        mCloseCallback();
        return true;
    }

    return false;
}

TEV_NAMESPACE_END
<commit_msg>Fix help entry<commit_after>// This file was developed by Thomas Müller <thomas94@gmx.net>.
// It is published under the BSD 3-Clause License within the LICENSE file.

#include <tev/HelpWindow.h>

#include <nanogui/button.h>
#include <nanogui/entypo.h>
#include <nanogui/label.h>
#include <nanogui/layout.h>
#include <nanogui/opengl.h>
#include <nanogui/tabwidget.h>
#include <nanogui/window.h>

using namespace nanogui;
using namespace std;

TEV_NAMESPACE_BEGIN

#ifdef __APPLE__
string HelpWindow::COMMAND = "Cmd";
#else
string HelpWindow::COMMAND = "Ctrl";
#endif

#ifdef __APPLE__
string HelpWindow::ALT = "Opt";
#else
string HelpWindow::ALT = "Alt";
#endif

HelpWindow::HelpWindow(Widget *parent, function<void()> closeCallback)
    : Window{parent, "Help"}, mCloseCallback{closeCallback} {

    auto closeButton = new Button{buttonPanel(), "", ENTYPO_ICON_CROSS};
    closeButton->setCallback(mCloseCallback);

    setLayout(new GroupLayout{});
    setFixedWidth(600);

    TabWidget* tabWidget = new TabWidget{this};

    // Keybindings tab
    {
        Widget* shortcuts = tabWidget->createTab("Keybindings");
        shortcuts->setLayout(new GroupLayout{});

        auto addRow = [](Widget* current, string keys, string desc) {
            auto row = new Widget{current};
            row->setLayout(new BoxLayout{Orientation::Horizontal, Alignment::Fill, 0, 10});
            auto descWidget = new Label{row, desc, "sans"};
            descWidget->setFixedWidth(250);
            new Label{row, keys, "sans-bold"};
        };

        auto addSpacer = [](Widget* current) {
            auto row = new Widget{current};
            row->setHeight(10);
        };

        new Label{shortcuts, "Image Loading", "sans-bold", 18};
        auto imageLoading = new Widget{shortcuts};
        imageLoading->setLayout(new BoxLayout{Orientation::Vertical, Alignment::Fill, 0, 0});

        addRow(imageLoading, COMMAND + "+O",                             "Open Image");
        addRow(imageLoading, COMMAND + "+S",                             "Save View as Image");
        addRow(imageLoading, COMMAND + "+R or F5",                       "Reload Image");
        addRow(imageLoading, COMMAND + "+Shift+R or " + COMMAND + "+F5", "Reload All Images");
        addRow(imageLoading, COMMAND + "+W",                             "Close Image");

        new Label{shortcuts, "Image Options", "sans-bold", 18};
        auto imageSelection = new Widget{shortcuts};
        imageSelection->setLayout(new BoxLayout{Orientation::Vertical, Alignment::Fill, 0, 0});

        addRow(imageSelection, "Left Click",          "Select Hovered Image");
        addRow(imageSelection, "1…9",                 "Select N-th Image");
        addRow(imageSelection, "Down or S / Up or W", "Select Next / Previous Image");

        addSpacer(imageSelection);

        addRow(imageSelection, "F", "Fit Image to Screen");
        addRow(imageSelection, "N", "Normalize Image to [0, 1]");
        addRow(imageSelection, "R", "Reset Image Parameters");

        addSpacer(imageSelection);

        addRow(imageSelection, "Shift+Right or Shift+D / Shift+Left or Shift+A", "Select Next / Previous Tonemap");

        addSpacer(imageSelection);

        addRow(imageSelection, "E / Shift+E", "Increase / Decrease Exposure by 0.5");
        addRow(imageSelection, "O / Shift+O", "Increase / Decrease Offset by 0.1");

        new Label{shortcuts, "Reference Options", "sans-bold", 18};
        auto referenceSelection = new Widget{shortcuts};
        referenceSelection->setLayout(new BoxLayout{Orientation::Vertical, Alignment::Fill, 0, 0});

        addRow(referenceSelection, "Shift+Left Click or Right Click",             "Select Hovered Image as Reference");
        addRow(referenceSelection, "Shift+1…9",                                   "Select N-th Image as Reference");
        addRow(referenceSelection, "Shift+Down or Shift+S / Shift+Up or Shift+W", "Select Next / Previous Image as Reference");

        addSpacer(referenceSelection);

        addRow(referenceSelection, "Ctrl+Right or Ctrl+D / Ctrl+Left or Ctrl+A", "Select Next / Previous Error Metric");

        new Label{shortcuts, "Layer Options", "sans-bold", 18};
        auto layerSelection = new Widget{shortcuts};
        layerSelection->setLayout(new BoxLayout{Orientation::Vertical, Alignment::Fill, 0, 0});

        addRow(layerSelection, "Left Click",             "Select Hovered Layer");
        addRow(layerSelection, "Ctrl+1…9",               "Select N-th Layer");
        addRow(layerSelection, "Right or D / Left or A", "Select Next / Previous Layer");

        new Label{shortcuts, "Interface", "sans-bold", 18};
        auto ui = new Widget{shortcuts};
        ui->setLayout(new BoxLayout{Orientation::Vertical, Alignment::Fill, 0, 0});

        addRow(ui, ALT + "+Enter", "Maximize");
        addRow(ui, COMMAND + "+B", "Toggle GUI");
        addRow(ui, "H",            "Show Help (this Window)");
        addRow(ui, COMMAND + "+P", "Find Image or Layer");
        addRow(ui, "Q or Esc",     "Quit");
    }

    // About tab
    {
        Widget* about = tabWidget->createTab("About");
        about->setLayout(new GroupLayout{});

        auto addText = [](Widget* current, string text, string font = "sans", int fontSize = 18) {
            auto row = new Widget{current};
            row->setLayout(new BoxLayout{Orientation::Vertical, Alignment::Middle, 0, 10});
            new Label{row, text, font, fontSize };
        };

        auto addLibrary = [](Widget* current, string name, string license, string desc) {
            auto row = new Widget{current};
            row->setLayout(new BoxLayout{Orientation::Horizontal, Alignment::Fill, 5, 30});
            auto leftColumn = new Widget{row};
            leftColumn->setLayout(new BoxLayout{Orientation::Vertical, Alignment::Maximum});
            leftColumn->setFixedWidth(130);

            new Label{leftColumn, name, "sans-bold", 18};
            new Label{row, desc, "sans", 18};
        };

        auto addSpacer = [](Widget* current, int space) {
            auto row = new Widget{current};
            row->setHeight(space);
        };

        addSpacer(about, 15);

        addText(about, "tev — The EXR Viewer", "sans-bold", 46);
        addText(about, "version " TEV_VERSION, "sans", 26);

        addSpacer(about, 60);

        addText(about, "tev was developed by Thomas Müller and is released under the BSD 3-Clause License.");
        addText(about, "It was built directly or indirectly upon the following amazing third-party libraries.");

        addSpacer(about, 40);

        addLibrary(about, "args",              "", "Single-Header Argument Parsing Library.");
        addLibrary(about, "Eigen",             "", "C++ Template Library for Linear Algebra.");
        addLibrary(about, "Glad",              "", "Multi-Language GL Loader-Generator.");
        addLibrary(about, "GLEW",              "", "The OpenGL Extension Wrangler Library.");
        addLibrary(about, "GLFW",              "", "OpenGL Desktop Development Library.");
        addLibrary(about, "NanoGUI",           "", "Small Widget Library for OpenGL.");
        addLibrary(about, "NanoVG",            "", "Small Vector Graphics Library.");
        addLibrary(about, "OpenEXR",           "", "High Dynamic-Range (HDR) Image File Format.");
        addLibrary(about, "stb_image(_write)", "", "Single-Header Library for Loading and Writing Images.");
        addLibrary(about, "tinyformat",        "", "Minimal Type-Safe printf() Replacement.");
    }

    tabWidget->setActiveTab(0);
}

bool HelpWindow::keyboardEvent(int key, int scancode, int action, int modifiers) {
    if (Window::keyboardEvent(key, scancode, action, modifiers)) {
        return true;
    }

    if (key == GLFW_KEY_ESCAPE) {
        mCloseCallback();
        return true;
    }

    return false;
}

TEV_NAMESPACE_END
<|endoftext|>


<commit_before>//===-- GlobalStatus.cpp - Compute status info for globals -----------------==//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//

#include "llvm/Transforms/Utils/GlobalStatus.h"
#include "llvm/ADT/SmallPtrSet.h"
#include "llvm/IR/BasicBlock.h"
#include "llvm/IR/CallSite.h"
#include "llvm/IR/Constant.h"
#include "llvm/IR/Constants.h"
#include "llvm/IR/GlobalValue.h"
#include "llvm/IR/GlobalVariable.h"
#include "llvm/IR/InstrTypes.h"
#include "llvm/IR/Instruction.h"
#include "llvm/IR/Instructions.h"
#include "llvm/IR/IntrinsicInst.h"
#include "llvm/IR/Use.h"
#include "llvm/IR/User.h"
#include "llvm/IR/Value.h"
#include "llvm/Support/AtomicOrdering.h"
#include "llvm/Support/Casting.h"
#include <algorithm>
#include <cassert>

using namespace llvm;

/// Return the stronger of the two ordering. If the two orderings are acquire
/// and release, then return AcquireRelease.
///
static AtomicOrdering strongerOrdering(AtomicOrdering X, AtomicOrdering Y) {
  if ((X == AtomicOrdering::Acquire && Y == AtomicOrdering::Release) ||
      (Y == AtomicOrdering::Acquire && X == AtomicOrdering::Release))
    return AtomicOrdering::AcquireRelease;
  return (AtomicOrdering)std::max((unsigned)X, (unsigned)Y);
}

/// It is safe to destroy a constant iff it is only used by constants itself.
/// Note that constants cannot be cyclic, so this test is pretty easy to
/// implement recursively.
///
bool llvm::isSafeToDestroyConstant(const Constant *C) {
  if (isa<GlobalValue>(C))
    return false;

  if (isa<ConstantData>(C))
    return false;

  for (const User *U : C->users())
    if (const Constant *CU = dyn_cast<Constant>(U)) {
      if (!isSafeToDestroyConstant(CU))
        return false;
    } else
      return false;
  return true;
}

static bool analyzeGlobalAux(const Value *V, GlobalStatus &GS,
                             SmallPtrSetImpl<const PHINode *> &PhiUsers) {
  if (const GlobalVariable *GV = dyn_cast<GlobalVariable>(V))
    if (GV->isExternallyInitialized())
      GS.StoredType = GlobalStatus::StoredOnce;

  for (const Use &U : V->uses()) {
    const User *UR = U.getUser();
    if (const ConstantExpr *CE = dyn_cast<ConstantExpr>(UR)) {
      GS.HasNonInstructionUser = true;

      // If the result of the constantexpr isn't pointer type, then we won't
      // know to expect it in various places.  Just reject early.
      if (!isa<PointerType>(CE->getType()))
        return true;

      if (analyzeGlobalAux(CE, GS, PhiUsers))
        return true;
    } else if (const Instruction *I = dyn_cast<Instruction>(UR)) {
      if (!GS.HasMultipleAccessingFunctions) {
        const Function *F = I->getParent()->getParent();
        if (!GS.AccessingFunction)
          GS.AccessingFunction = F;
        else if (GS.AccessingFunction != F)
          GS.HasMultipleAccessingFunctions = true;
      }
      if (const LoadInst *LI = dyn_cast<LoadInst>(I)) {
        GS.IsLoaded = true;
        // Don't hack on volatile loads.
        if (LI->isVolatile())
          return true;
        GS.Ordering = strongerOrdering(GS.Ordering, LI->getOrdering());
      } else if (const StoreInst *SI = dyn_cast<StoreInst>(I)) {
        // Don't allow a store OF the address, only stores TO the address.
        if (SI->getOperand(0) == V)
          return true;

        // Don't hack on volatile stores.
        if (SI->isVolatile())
          return true;

        GS.Ordering = strongerOrdering(GS.Ordering, SI->getOrdering());

        // If this is a direct store to the global (i.e., the global is a scalar
        // value, not an aggregate), keep more specific information about
        // stores.
        if (GS.StoredType != GlobalStatus::Stored) {
          if (const GlobalVariable *GV =
                  dyn_cast<GlobalVariable>(SI->getOperand(1))) {
            Value *StoredVal = SI->getOperand(0);

            if (Constant *C = dyn_cast<Constant>(StoredVal)) {
              if (C->isThreadDependent()) {
                // The stored value changes between threads; don't track it.
                return true;
              }
            }

            if (GV->hasInitializer() && StoredVal == GV->getInitializer()) {
              if (GS.StoredType < GlobalStatus::InitializerStored)
                GS.StoredType = GlobalStatus::InitializerStored;
            } else if (isa<LoadInst>(StoredVal) &&
                       cast<LoadInst>(StoredVal)->getOperand(0) == GV) {
              if (GS.StoredType < GlobalStatus::InitializerStored)
                GS.StoredType = GlobalStatus::InitializerStored;
            } else if (GS.StoredType < GlobalStatus::StoredOnce) {
              GS.StoredType = GlobalStatus::StoredOnce;
              GS.StoredOnceValue = StoredVal;
            } else if (GS.StoredType == GlobalStatus::StoredOnce &&
                       GS.StoredOnceValue == StoredVal) {
              // noop.
            } else {
              GS.StoredType = GlobalStatus::Stored;
            }
          } else {
            GS.StoredType = GlobalStatus::Stored;
          }
        }
      } else if (isa<BitCastInst>(I)) {
        if (analyzeGlobalAux(I, GS, PhiUsers))
          return true;
      } else if (isa<GetElementPtrInst>(I)) {
        if (analyzeGlobalAux(I, GS, PhiUsers))
          return true;
      } else if (isa<SelectInst>(I)) {
        if (analyzeGlobalAux(I, GS, PhiUsers))
          return true;
      } else if (const PHINode *PN = dyn_cast<PHINode>(I)) {
        // PHI nodes we can check just like select or GEP instructions, but we
        // have to be careful about infinite recursion.
        if (PhiUsers.insert(PN).second) // Not already visited.
          if (analyzeGlobalAux(I, GS, PhiUsers))
            return true;
      } else if (isa<CmpInst>(I)) {
        GS.IsCompared = true;
      } else if (const MemTransferInst *MTI = dyn_cast<MemTransferInst>(I)) {
        if (MTI->isVolatile())
          return true;
        if (MTI->getArgOperand(0) == V)
          GS.StoredType = GlobalStatus::Stored;
        if (MTI->getArgOperand(1) == V)
          GS.IsLoaded = true;
      } else if (const MemSetInst *MSI = dyn_cast<MemSetInst>(I)) {
        assert(MSI->getArgOperand(0) == V && "Memset only takes one pointer!");
        if (MSI->isVolatile())
          return true;
        GS.StoredType = GlobalStatus::Stored;
      } else if (auto C = ImmutableCallSite(I)) {
        if (!C.isCallee(&U))
          return true;
        GS.IsLoaded = true;
      } else {
        return true; // Any other non-load instruction might take address!
      }
    } else if (const Constant *C = dyn_cast<Constant>(UR)) {
      GS.HasNonInstructionUser = true;
      // We might have a dead and dangling constant hanging off of here.
      if (!isSafeToDestroyConstant(C))
        return true;
    } else {
      GS.HasNonInstructionUser = true;
      // Otherwise must be some other user.
      return true;
    }
  }

  return false;
}

GlobalStatus::GlobalStatus() = default;

bool GlobalStatus::analyzeGlobal(const Value *V, GlobalStatus &GS) {
  SmallPtrSet<const PHINode *, 16> PhiUsers;
  return analyzeGlobalAux(V, GS, PhiUsers);
}
<commit_msg>[GlobalOpt] Fix exponential compile-time with selects.<commit_after>//===-- GlobalStatus.cpp - Compute status info for globals -----------------==//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//

#include "llvm/Transforms/Utils/GlobalStatus.h"
#include "llvm/ADT/SmallPtrSet.h"
#include "llvm/IR/BasicBlock.h"
#include "llvm/IR/CallSite.h"
#include "llvm/IR/Constant.h"
#include "llvm/IR/Constants.h"
#include "llvm/IR/GlobalValue.h"
#include "llvm/IR/GlobalVariable.h"
#include "llvm/IR/InstrTypes.h"
#include "llvm/IR/Instruction.h"
#include "llvm/IR/Instructions.h"
#include "llvm/IR/IntrinsicInst.h"
#include "llvm/IR/Use.h"
#include "llvm/IR/User.h"
#include "llvm/IR/Value.h"
#include "llvm/Support/AtomicOrdering.h"
#include "llvm/Support/Casting.h"
#include <algorithm>
#include <cassert>

using namespace llvm;

/// Return the stronger of the two ordering. If the two orderings are acquire
/// and release, then return AcquireRelease.
///
static AtomicOrdering strongerOrdering(AtomicOrdering X, AtomicOrdering Y) {
  if ((X == AtomicOrdering::Acquire && Y == AtomicOrdering::Release) ||
      (Y == AtomicOrdering::Acquire && X == AtomicOrdering::Release))
    return AtomicOrdering::AcquireRelease;
  return (AtomicOrdering)std::max((unsigned)X, (unsigned)Y);
}

/// It is safe to destroy a constant iff it is only used by constants itself.
/// Note that constants cannot be cyclic, so this test is pretty easy to
/// implement recursively.
///
bool llvm::isSafeToDestroyConstant(const Constant *C) {
  if (isa<GlobalValue>(C))
    return false;

  if (isa<ConstantData>(C))
    return false;

  for (const User *U : C->users())
    if (const Constant *CU = dyn_cast<Constant>(U)) {
      if (!isSafeToDestroyConstant(CU))
        return false;
    } else
      return false;
  return true;
}

static bool analyzeGlobalAux(const Value *V, GlobalStatus &GS,
                             SmallPtrSetImpl<const Value *> &VisitedUsers) {
  if (const GlobalVariable *GV = dyn_cast<GlobalVariable>(V))
    if (GV->isExternallyInitialized())
      GS.StoredType = GlobalStatus::StoredOnce;

  for (const Use &U : V->uses()) {
    const User *UR = U.getUser();
    if (const ConstantExpr *CE = dyn_cast<ConstantExpr>(UR)) {
      GS.HasNonInstructionUser = true;

      // If the result of the constantexpr isn't pointer type, then we won't
      // know to expect it in various places.  Just reject early.
      if (!isa<PointerType>(CE->getType()))
        return true;

      // FIXME: Do we need to add constexpr selects to VisitedUsers?
      if (analyzeGlobalAux(CE, GS, VisitedUsers))
        return true;
    } else if (const Instruction *I = dyn_cast<Instruction>(UR)) {
      if (!GS.HasMultipleAccessingFunctions) {
        const Function *F = I->getParent()->getParent();
        if (!GS.AccessingFunction)
          GS.AccessingFunction = F;
        else if (GS.AccessingFunction != F)
          GS.HasMultipleAccessingFunctions = true;
      }
      if (const LoadInst *LI = dyn_cast<LoadInst>(I)) {
        GS.IsLoaded = true;
        // Don't hack on volatile loads.
        if (LI->isVolatile())
          return true;
        GS.Ordering = strongerOrdering(GS.Ordering, LI->getOrdering());
      } else if (const StoreInst *SI = dyn_cast<StoreInst>(I)) {
        // Don't allow a store OF the address, only stores TO the address.
        if (SI->getOperand(0) == V)
          return true;

        // Don't hack on volatile stores.
        if (SI->isVolatile())
          return true;

        GS.Ordering = strongerOrdering(GS.Ordering, SI->getOrdering());

        // If this is a direct store to the global (i.e., the global is a scalar
        // value, not an aggregate), keep more specific information about
        // stores.
        if (GS.StoredType != GlobalStatus::Stored) {
          if (const GlobalVariable *GV =
                  dyn_cast<GlobalVariable>(SI->getOperand(1))) {
            Value *StoredVal = SI->getOperand(0);

            if (Constant *C = dyn_cast<Constant>(StoredVal)) {
              if (C->isThreadDependent()) {
                // The stored value changes between threads; don't track it.
                return true;
              }
            }

            if (GV->hasInitializer() && StoredVal == GV->getInitializer()) {
              if (GS.StoredType < GlobalStatus::InitializerStored)
                GS.StoredType = GlobalStatus::InitializerStored;
            } else if (isa<LoadInst>(StoredVal) &&
                       cast<LoadInst>(StoredVal)->getOperand(0) == GV) {
              if (GS.StoredType < GlobalStatus::InitializerStored)
                GS.StoredType = GlobalStatus::InitializerStored;
            } else if (GS.StoredType < GlobalStatus::StoredOnce) {
              GS.StoredType = GlobalStatus::StoredOnce;
              GS.StoredOnceValue = StoredVal;
            } else if (GS.StoredType == GlobalStatus::StoredOnce &&
                       GS.StoredOnceValue == StoredVal) {
              // noop.
            } else {
              GS.StoredType = GlobalStatus::Stored;
            }
          } else {
            GS.StoredType = GlobalStatus::Stored;
          }
        }
      } else if (isa<BitCastInst>(I) || isa<GetElementPtrInst>(I)) {
        // Skip over bitcasts and GEPs; we don't care about the type or offset
        // of the pointer.
        if (analyzeGlobalAux(I, GS, VisitedUsers))
          return true;
      } else if (isa<SelectInst>(I) || isa<PHINode>(I)) {
        // Look through selects and PHIs to find if the pointer is
        // conditionally accessed. Make sure we only visit an instruction
        // once; otherwise, we can get infinite recursion or exponential
        // compile time.
        if (VisitedUsers.insert(I).second)
          if (analyzeGlobalAux(I, GS, VisitedUsers))
            return true;
      } else if (isa<CmpInst>(I)) {
        GS.IsCompared = true;
      } else if (const MemTransferInst *MTI = dyn_cast<MemTransferInst>(I)) {
        if (MTI->isVolatile())
          return true;
        if (MTI->getArgOperand(0) == V)
          GS.StoredType = GlobalStatus::Stored;
        if (MTI->getArgOperand(1) == V)
          GS.IsLoaded = true;
      } else if (const MemSetInst *MSI = dyn_cast<MemSetInst>(I)) {
        assert(MSI->getArgOperand(0) == V && "Memset only takes one pointer!");
        if (MSI->isVolatile())
          return true;
        GS.StoredType = GlobalStatus::Stored;
      } else if (auto C = ImmutableCallSite(I)) {
        if (!C.isCallee(&U))
          return true;
        GS.IsLoaded = true;
      } else {
        return true; // Any other non-load instruction might take address!
      }
    } else if (const Constant *C = dyn_cast<Constant>(UR)) {
      GS.HasNonInstructionUser = true;
      // We might have a dead and dangling constant hanging off of here.
      if (!isSafeToDestroyConstant(C))
        return true;
    } else {
      GS.HasNonInstructionUser = true;
      // Otherwise must be some other user.
      return true;
    }
  }

  return false;
}

GlobalStatus::GlobalStatus() = default;

bool GlobalStatus::analyzeGlobal(const Value *V, GlobalStatus &GS) {
  SmallPtrSet<const Value *, 16> VisitedUsers;
  return analyzeGlobalAux(V, GS, VisitedUsers);
}
<|endoftext|>