<commit_before>/*

  Copyright 2009 University of Helsinki
  Copyright 2015 Tino Didriksen <mail@tinodidriksen.com>
  Code adapted from https://github.com/TinoDidriksen/trie-tools

  Licensed under the Apache License, Version 2.0 (the "License");
  you may not use this file except in compliance with the License.
  You may obtain a copy of the License at

  http://www.apache.org/licenses/LICENSE-2.0

  Unless required by applicable law or agreed to in writing, software
  distributed under the License is distributed on an "AS IS" BASIS,
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  See the License for the specific language governing permissions and
  limitations under the License.

*/

/*
	Tests up to 8 variations of each input token:
	- Verbatim
	- With leading punctuaton removed
	- With trailing punctuaton removed
	- With leading and trailing punctuaton removed
	- Lower-case of all the above
*/

#include <iostream>
#include <fstream>
#include <algorithm>
#include <map>
#include <sstream>
#include <cctype>
#include <cwctype>
#include <cmath>
#include <cerrno>

#include "ol-exceptions.h"
#include "ospell.h"
#include "ZHfstOspeller.h"

using hfst_ol::ZHfstOspeller;
using hfst_ol::Transducer;

typedef std::map<std::string,bool> valid_words_t;
valid_words_t valid_words;

struct word_t {
	size_t start, count;
	std::string buffer;
};
std::vector<word_t> words(8);
std::string buffer;
size_t cw;

bool find_alternatives(ZHfstOspeller& speller, size_t suggs) {
	/* Weights make this entirely pointless
	size_t dist = std::max(static_cast<size_t>(1), static_cast<size_t>(std::log(words[cw-1].buffer.size()) / std::log(2)));
	speller.set_weight_limit(dist);
	speller.set_beam(dist / 2);
	//*/

	for (size_t k=1 ; k <= cw ; ++k) {
		hfst_ol::CorrectionQueue corrections = speller.suggest(words[cw-k].buffer);

		if (corrections.size() == 0) {
			continue;
		}

		std::cout << "&";
		// Because speller.set_queue_limit() doesn't actually work, hard limit it here
		for (size_t i=0, e=corrections.size() ; i<e && i<suggs ; ++i) {
			std::cout << "\t";

			buffer.clear();
			if (cw - k != 0) {
				buffer.append(words[0].buffer.begin(), words[0].buffer.begin() + words[cw-k].start);
			}
			buffer.append(corrections.top().first);
			if (cw - k != 0) {
				buffer.append(words[0].buffer.begin() + words[cw-k].start + words[cw-k].count, words[0].buffer.end());
			}

			std::cout << buffer;
			corrections.pop();
		}
		std::cout << std::endl;
		return true;
	}
	return false;
}

bool is_valid_word(ZHfstOspeller& speller, const std::string& word) {
	size_t ichStart = 0, cchUse = word.size();
	const char *pwsz = word.c_str();

	// Always test the full given input
	words[0].buffer.resize(0);
	words[0].start = ichStart;
	words[0].count = cchUse;
	words[0].buffer.append(pwsz+ichStart, pwsz+ichStart+cchUse);
	cw = 1;

	if (cchUse > 1) {
		size_t count = cchUse;
		while (count && (std::iswpunct(pwsz[ichStart+count-1]) || std::iswspace(pwsz[ichStart+count-1]))) {
			--count;
		}
		if (count != cchUse) {
			// If the input ended with punctuation, test input with punctuation trimmed from the end
			words[cw].buffer.resize(0);
			words[cw].start = ichStart;
			words[cw].count = count;
			words[cw].buffer.append(pwsz+words[cw].start, pwsz+words[cw].start+words[cw].count);
			++cw;
		}

		size_t start = ichStart, count2 = cchUse;
		while (start < ichStart+cchUse && (std::iswpunct(pwsz[start]) || std::iswspace(pwsz[start]))) {
			++start;
			--count2;
		}
		if (start != ichStart) {
			// If the input started with punctuation, test input with punctuation trimmed from the start
			words[cw].buffer.resize(0);
			words[cw].start = start;
			words[cw].count = count2;
			words[cw].buffer.append(pwsz+words[cw].start, pwsz+words[cw].start+words[cw].count);
			++cw;
		}

		if (start != ichStart && count != cchUse) {
			// If the input both started and ended with punctuation, test input with punctuation trimmed from both sides
			words[cw].buffer.resize(0);
			words[cw].start = start;
			words[cw].count = count - (cchUse - count2);
			words[cw].buffer.append(pwsz+words[cw].start, pwsz+words[cw].start+words[cw].count);
			++cw;
		}
	}

	for (size_t i=0, e=cw ; i<e ; ++i) {
		typename valid_words_t::iterator it = valid_words.find(words[i].buffer);

		if (it == valid_words.end()) {
			bool valid = speller.spell(words[i].buffer);
			it = valid_words.insert(std::make_pair(words[i].buffer,valid)).first;

			if (!valid) {
				// If the word was not valid, fold it to lower case and try again
				buffer.resize(0);
				buffer.reserve(words[i].buffer.size());
				std::transform(words[i].buffer.begin(), words[i].buffer.end(), std::back_inserter(buffer), std::towlower);

				// Add the lower case variant to the list so that we get suggestions using that, if need be
				words[cw].start = words[i].start;
				words[cw].count = words[i].count;
				words[cw].buffer = buffer;
				++cw;

				// Don't try again if the lower cased variant has already been tried
				typename valid_words_t::iterator itl = valid_words.find(buffer);
				if (itl != valid_words.end()) {
					it->second = itl->second;
					it = itl;
				}
				else {
					valid = speller.spell(buffer);
					it->second = valid; // Also mark the original mixed case variant as whatever the lower cased one was
					it = valid_words.insert(std::make_pair(buffer,valid)).first;
				}
			}
		}

		if (it->second == true) {
			return true;
		}
	}

	return false;
}

int zhfst_spell(const char* zhfst_filename) {
	ZHfstOspeller speller;
	try {
		speller.read_zhfst(zhfst_filename);
	}
	catch (hfst_ol::ZHfstMetaDataParsingError zhmdpe) {
		fprintf(stderr, "cannot finish reading zhfst archive %s:\n%s.\n", zhfst_filename, zhmdpe.what());
		return EXIT_FAILURE;
	}
	catch (hfst_ol::ZHfstZipReadingError zhzre) {
		fprintf(stderr, "cannot read zhfst archive %s:\n%s.\n", zhfst_filename, zhzre.what());
		return EXIT_FAILURE;
	}
	catch (hfst_ol::ZHfstXmlParsingError zhxpe) {
		fprintf(stderr, "Cannot finish reading index.xml from %s:\n%s.\n", zhfst_filename, zhxpe.what());
		return EXIT_FAILURE;
	}

	std::cout << "@@ hfst-ospell-office is alive" << std::endl;

	std::string line;
	std::string word;
	std::istringstream ss;
	while (std::getline(std::cin, line)) {
		while (!line.empty() && std::iswspace(line[line.size()-1])) {
			line.resize(line.size()-1);
		}
		if (line.empty()) {
			continue;
		}

		ss.clear();
		ss.str(line);
		size_t suggs = 0;
		char c = 0;
		if (!(ss >> suggs) || !ss.get(c) || !std::getline(ss, line)) {
			std::cout << "!" << std::endl;
			continue;
		}

		if (is_valid_word(speller, line)) {
			std::cout << "*" << std::endl;
			continue;
		}

		if (!suggs || !find_alternatives(speller, suggs)) {
			std::cout << "#" << std::endl;
		}
	}
    return EXIT_SUCCESS;
}

int main(int, char **argv) {
	return zhfst_spell(argv[1]);
}
<commit_msg>Removed keyword 'typename', it caused GCC with C++03 to bark.<commit_after>/*

  Copyright 2009 University of Helsinki
  Copyright 2015 Tino Didriksen <mail@tinodidriksen.com>
  Code adapted from https://github.com/TinoDidriksen/trie-tools

  Licensed under the Apache License, Version 2.0 (the "License");
  you may not use this file except in compliance with the License.
  You may obtain a copy of the License at

  http://www.apache.org/licenses/LICENSE-2.0

  Unless required by applicable law or agreed to in writing, software
  distributed under the License is distributed on an "AS IS" BASIS,
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  See the License for the specific language governing permissions and
  limitations under the License.

*/

/*
	Tests up to 8 variations of each input token:
	- Verbatim
	- With leading punctuaton removed
	- With trailing punctuaton removed
	- With leading and trailing punctuaton removed
	- Lower-case of all the above
*/

#include <iostream>
#include <fstream>
#include <algorithm>
#include <map>
#include <sstream>
#include <cctype>
#include <cwctype>
#include <cmath>
#include <cerrno>

#include "ol-exceptions.h"
#include "ospell.h"
#include "ZHfstOspeller.h"

using hfst_ol::ZHfstOspeller;
using hfst_ol::Transducer;

typedef std::map<std::string,bool> valid_words_t;
valid_words_t valid_words;

struct word_t {
	size_t start, count;
	std::string buffer;
};
std::vector<word_t> words(8);
std::string buffer;
size_t cw;

bool find_alternatives(ZHfstOspeller& speller, size_t suggs) {
	/* Weights make this entirely pointless
	size_t dist = std::max(static_cast<size_t>(1), static_cast<size_t>(std::log(words[cw-1].buffer.size()) / std::log(2)));
	speller.set_weight_limit(dist);
	speller.set_beam(dist / 2);
	//*/

	for (size_t k=1 ; k <= cw ; ++k) {
		hfst_ol::CorrectionQueue corrections = speller.suggest(words[cw-k].buffer);

		if (corrections.size() == 0) {
			continue;
		}

		std::cout << "&";
		// Because speller.set_queue_limit() doesn't actually work, hard limit it here
		for (size_t i=0, e=corrections.size() ; i<e && i<suggs ; ++i) {
			std::cout << "\t";

			buffer.clear();
			if (cw - k != 0) {
				buffer.append(words[0].buffer.begin(), words[0].buffer.begin() + words[cw-k].start);
			}
			buffer.append(corrections.top().first);
			if (cw - k != 0) {
				buffer.append(words[0].buffer.begin() + words[cw-k].start + words[cw-k].count, words[0].buffer.end());
			}

			std::cout << buffer;
			corrections.pop();
		}
		std::cout << std::endl;
		return true;
	}
	return false;
}

bool is_valid_word(ZHfstOspeller& speller, const std::string& word) {
	size_t ichStart = 0, cchUse = word.size();
	const char *pwsz = word.c_str();

	// Always test the full given input
	words[0].buffer.resize(0);
	words[0].start = ichStart;
	words[0].count = cchUse;
	words[0].buffer.append(pwsz+ichStart, pwsz+ichStart+cchUse);
	cw = 1;

	if (cchUse > 1) {
		size_t count = cchUse;
		while (count && (std::iswpunct(pwsz[ichStart+count-1]) || std::iswspace(pwsz[ichStart+count-1]))) {
			--count;
		}
		if (count != cchUse) {
			// If the input ended with punctuation, test input with punctuation trimmed from the end
			words[cw].buffer.resize(0);
			words[cw].start = ichStart;
			words[cw].count = count;
			words[cw].buffer.append(pwsz+words[cw].start, pwsz+words[cw].start+words[cw].count);
			++cw;
		}

		size_t start = ichStart, count2 = cchUse;
		while (start < ichStart+cchUse && (std::iswpunct(pwsz[start]) || std::iswspace(pwsz[start]))) {
			++start;
			--count2;
		}
		if (start != ichStart) {
			// If the input started with punctuation, test input with punctuation trimmed from the start
			words[cw].buffer.resize(0);
			words[cw].start = start;
			words[cw].count = count2;
			words[cw].buffer.append(pwsz+words[cw].start, pwsz+words[cw].start+words[cw].count);
			++cw;
		}

		if (start != ichStart && count != cchUse) {
			// If the input both started and ended with punctuation, test input with punctuation trimmed from both sides
			words[cw].buffer.resize(0);
			words[cw].start = start;
			words[cw].count = count - (cchUse - count2);
			words[cw].buffer.append(pwsz+words[cw].start, pwsz+words[cw].start+words[cw].count);
			++cw;
		}
	}

	for (size_t i=0, e=cw ; i<e ; ++i) {
		valid_words_t::iterator it = valid_words.find(words[i].buffer);

		if (it == valid_words.end()) {
			bool valid = speller.spell(words[i].buffer);
			it = valid_words.insert(std::make_pair(words[i].buffer,valid)).first;

			if (!valid) {
				// If the word was not valid, fold it to lower case and try again
				buffer.resize(0);
				buffer.reserve(words[i].buffer.size());
				std::transform(words[i].buffer.begin(), words[i].buffer.end(), std::back_inserter(buffer), std::towlower);

				// Add the lower case variant to the list so that we get suggestions using that, if need be
				words[cw].start = words[i].start;
				words[cw].count = words[i].count;
				words[cw].buffer = buffer;
				++cw;

				// Don't try again if the lower cased variant has already been tried
				valid_words_t::iterator itl = valid_words.find(buffer);
				if (itl != valid_words.end()) {
					it->second = itl->second;
					it = itl;
				}
				else {
					valid = speller.spell(buffer);
					it->second = valid; // Also mark the original mixed case variant as whatever the lower cased one was
					it = valid_words.insert(std::make_pair(buffer,valid)).first;
				}
			}
		}

		if (it->second == true) {
			return true;
		}
	}

	return false;
}

int zhfst_spell(const char* zhfst_filename) {
	ZHfstOspeller speller;
	try {
		speller.read_zhfst(zhfst_filename);
	}
	catch (hfst_ol::ZHfstMetaDataParsingError zhmdpe) {
		fprintf(stderr, "cannot finish reading zhfst archive %s:\n%s.\n", zhfst_filename, zhmdpe.what());
		return EXIT_FAILURE;
	}
	catch (hfst_ol::ZHfstZipReadingError zhzre) {
		fprintf(stderr, "cannot read zhfst archive %s:\n%s.\n", zhfst_filename, zhzre.what());
		return EXIT_FAILURE;
	}
	catch (hfst_ol::ZHfstXmlParsingError zhxpe) {
		fprintf(stderr, "Cannot finish reading index.xml from %s:\n%s.\n", zhfst_filename, zhxpe.what());
		return EXIT_FAILURE;
	}

	std::cout << "@@ hfst-ospell-office is alive" << std::endl;

	std::string line;
	std::string word;
	std::istringstream ss;
	while (std::getline(std::cin, line)) {
		while (!line.empty() && std::iswspace(line[line.size()-1])) {
			line.resize(line.size()-1);
		}
		if (line.empty()) {
			continue;
		}

		ss.clear();
		ss.str(line);
		size_t suggs = 0;
		char c = 0;
		if (!(ss >> suggs) || !ss.get(c) || !std::getline(ss, line)) {
			std::cout << "!" << std::endl;
			continue;
		}

		if (is_valid_word(speller, line)) {
			std::cout << "*" << std::endl;
			continue;
		}

		if (!suggs || !find_alternatives(speller, suggs)) {
			std::cout << "#" << std::endl;
		}
	}
    return EXIT_SUCCESS;
}

int main(int, char **argv) {
	return zhfst_spell(argv[1]);
}
<|endoftext|>


<commit_before>/*
 *  ColorPickerItem.cpp
 *  buttons
 *
 *  Created by timknapen on 30/02/18.
 *  Copyright 2010 Indianen. All rights reserved.
 *
 */

#include "ColorPickerItem.h"

//--------------------------------------------------------------
ColorPickerItem::ColorPickerItem(string theTitle, ofPoint& _color, float saturation):ListItem(theTitle){
	// cout << "making new sliderItem: "<< theTitle << endl;
	width = 30 + 4 + title.length()*CHAR_W; //textw
	width = (width < 300) ? 300 : width;
	int imgw = width - 16;
	int imgh = height;
	color = &_color;
	img.allocate(imgw, imgh, OF_IMAGE_COLOR);
	// set the colors
	ofPixels pix = img.getPixels();
	int id;
	float xpct, ypct;
	for(int x = 0; x < imgw; x++){
		for(int y = 0; y < imgh; y++){
			id = x + y * imgw;
			ofColor newColor;
			xpct = (float)x/(float)(imgw-10);
			ypct = (float)y/(float)imgh;

			if( x < imgw - 10){
				newColor.setHsb( 255 * xpct,		// H
								saturation * 255 * ((ypct < 0.5) ?  2 * ypct : 1 ), // 1 - 2 *(ypct - 0.5))),		 // S
								255 * ((ypct < 0.5) ? 1 :  1 - 2 *(ypct - 0.5))	// B
								);
			}else if(x == imgw - 10){
				newColor.set(255, 255, 255);
			}else{
				float l = ceil(255.0 * (1.0 - ypct) );
				newColor.set(l, l, l );
			}
			pix[3 * id] = newColor.r;
			pix[3 * id + 1] = newColor.g;
			pix[3 * id + 2] = newColor.b;
		}
	}
	img.setFromPixels(pix);
}

//-------------------------------------------------
void ColorPickerItem::saveToXML( ofxXmlSettings& XML){
	// nothing yet
	XML.addTag("ColorPickerItem");
	int num = XML.getNumTags("ColorPickerItem") - 1;
	XML.setAttribute("ColorPickerItem", "name", title, num);
	XML.setAttribute("ColorPickerItem", "r", ofToString(color->x, 0), num);
	XML.setAttribute("ColorPickerItem", "g", ofToString(color->y, 0), num);
	XML.setAttribute("ColorPickerItem", "b", ofToString(color->z, 0), num);

}

//----------------------------------------------------------------
void ColorPickerItem::setValue(ofPoint _color){
	*color = _color;
}

//----------------------------------------------------------------
void ColorPickerItem::setValue(float _val){
	//*val = _color; do nothing
}

//--------------------------------------------------------------
bool ColorPickerItem::checkClick(int x, int y){
	ofPoint imgPos( x - 10, y - ypos ); // the image is drawn at 10, 0
	int imgw = img.getWidth();
	int imgh = img.getHeight();
	selected = false;
	if(imgPos.x > 0 && imgPos.x < imgw && imgPos.y > 0 && imgPos.y < imgh){
		selected = true;
	}
	if (selected) {
		ofPixels pix = img.getPixels();
		int id = (int)imgPos.x + (int)imgPos.y * imgw;
		color->set(pix[3*id], pix[3*id+1], pix[3*id+2]);
	}
	return selected;
}
//

//--------------------------------------------------------------
void ColorPickerItem::drag(int x, int y){
	ofPoint imgPos( x - 10, y - ypos); // the image is drawn at 10, 0
	int imgw = img.getWidth();
	int imgh = img.getHeight();
	if(imgPos.x < 0){
		imgPos.x = 0;
	}
	if(imgPos.x >= imgw){
		imgPos.x = imgw-1;
	}
	if(imgPos.y < 0){
		imgPos.y = 0;
	}
	if(imgPos.y >= imgh){
		imgPos.y = imgh-1;
	}

	if(imgPos.x > 0 && imgPos.x < imgw && imgPos.y > 0 && imgPos.y < imgh){
		ofPixels pix = img.getPixels();
		int id = (int)imgPos.x + (int)imgPos.y * imgw;
		color->set(pix[3*id], pix[3*id+1], pix[3*id+2]);
	}

	//*value = bottom + (top-bottom)*t;
}

//--------------------------------------------------------------
void ColorPickerItem::printValue(){
	cout << "  " << title << " = " << (int)(color->x) << ", " << (int)(color->y ) << ", " << (int)(color->z) << endl;
}

//--------------------------------------------------------------
void ColorPickerItem::draw(){

	ofPushMatrix();
	ofTranslate(0, ypos);
	// draw the sample color
	ofFill();
	ofSetColor(color->x, color->y, color->z);
	ofDrawRectangle(0, 0, 8, height);
	// draw the image at 10, ypos
	ofSetColor(255);
	img.draw(10, 0);
	if(over){
		ofSetHexColor(0xffffff);
	}else{
		ofSetHexColor(0x000000);
	}
	ofDrawBitmapString(title+": "+ofToString(color->x, 0)+
					   ", " + ofToString(color->y, 0) +
					   ", " + ofToString(color->z, 0), 12, height-4);
	ofPopMatrix();

}

<commit_msg>Fixed little bug : dragging and clicking edgecase x = 0, y = 0 was not working.<commit_after>/*
 *  ColorPickerItem.cpp
 *  buttons
 *
 *  Created by timknapen on 30/02/18.
 *  Copyright 2010 Indianen. All rights reserved.
 *
 */

#include "ColorPickerItem.h"

//--------------------------------------------------------------
ColorPickerItem::ColorPickerItem(string theTitle, ofPoint& _color, float saturation):ListItem(theTitle){
	// cout << "making new sliderItem: "<< theTitle << endl;
	width = 30 + 4 + title.length()*CHAR_W; //textw
	width = (width < 300) ? 300 : width;
	int imgw = width - 16;
	int imgh = height;
	color = &_color;
	img.allocate(imgw, imgh, OF_IMAGE_COLOR);
	// set the colors
	ofPixels pix = img.getPixels();
	int id;
	float xpct, ypct;
	for(int x = 0; x < imgw; x++){
		for(int y = 0; y < imgh; y++){
			id = x + y * imgw;
			ofColor newColor;
			xpct = (float)x/(float)(imgw-10);
			ypct = (float)y/(float)imgh;

			if( x < imgw - 10){
				newColor.setHsb( 255 * xpct,		// H
								saturation * 255 * ((ypct < 0.5) ?  2 * ypct : 1 ), // 1 - 2 *(ypct - 0.5))),		 // S
								255 * ((ypct < 0.5) ? 1 :  1 - 2 *(ypct - 0.5))	// B
								);
			}else if(x == imgw - 10){
				newColor.set(255, 255, 255);
			}else{
				float l = ceil(255.0 * (1.0 - ypct) );
				newColor.set(l, l, l );
			}
			pix[3 * id] = newColor.r;
			pix[3 * id + 1] = newColor.g;
			pix[3 * id + 2] = newColor.b;
		}
	}
	img.setFromPixels(pix);
}

//-------------------------------------------------
void ColorPickerItem::saveToXML( ofxXmlSettings& XML){
	// nothing yet
	XML.addTag("ColorPickerItem");
	int num = XML.getNumTags("ColorPickerItem") - 1;
	XML.setAttribute("ColorPickerItem", "name", title, num);
	XML.setAttribute("ColorPickerItem", "r", ofToString(color->x, 0), num);
	XML.setAttribute("ColorPickerItem", "g", ofToString(color->y, 0), num);
	XML.setAttribute("ColorPickerItem", "b", ofToString(color->z, 0), num);

}

//----------------------------------------------------------------
void ColorPickerItem::setValue(ofPoint _color){
	*color = _color;
}

//----------------------------------------------------------------
void ColorPickerItem::setValue(float _val){
	//*val = _color; do nothing
}

//--------------------------------------------------------------
bool ColorPickerItem::checkClick(int x, int y){
	ofPoint imgPos( x - 10, y - ypos ); // the image is drawn at 10, 0
	int imgw = img.getWidth();
	int imgh = img.getHeight();
	selected = false;
	if(imgPos.x >= 0 && imgPos.x < imgw && imgPos.y >= 0 && imgPos.y < imgh){
		selected = true;
	}
	if (selected) {
		ofPixels pix = img.getPixels();
		int id = (int)imgPos.x + (int)imgPos.y * imgw;
		color->set(pix[3*id], pix[3*id+1], pix[3*id+2]);
	}
	return selected;
}
//

//--------------------------------------------------------------
void ColorPickerItem::drag(int x, int y){
	ofPoint imgPos( x - 10, y - ypos); // the image is drawn at 10, 0
	int imgw = img.getWidth();
	int imgh = img.getHeight();
	if(imgPos.x < 0){
		imgPos.x = 0;
	}
	if(imgPos.x >= imgw){
		imgPos.x = imgw-1;
	}
	if(imgPos.y < 0){
		imgPos.y = 0;
	}
	if(imgPos.y >= imgh){
		imgPos.y = imgh-1;
	}

	if(imgPos.x >= 0 && imgPos.x < imgw && imgPos.y >= 0 && imgPos.y < imgh){
		ofPixels pix = img.getPixels();
		int id = (int)imgPos.x + (int)imgPos.y * imgw;
		color->set(pix[3*id], pix[3*id+1], pix[3*id+2]);
	}

	//*value = bottom + (top-bottom)*t;
}

//--------------------------------------------------------------
void ColorPickerItem::printValue(){
	cout << "  " << title << " = " << (int)(color->x) << ", " << (int)(color->y ) << ", " << (int)(color->z) << endl;
}

//--------------------------------------------------------------
void ColorPickerItem::draw(){

	ofPushMatrix();
	ofTranslate(0, ypos);
	// draw the sample color
	ofFill();
	ofSetColor(color->x, color->y, color->z);
	ofDrawRectangle(0, 0, 8, height);
	// draw the image at 10, ypos
	ofSetColor(255);
	img.draw(10, 0);
	if(over){
		ofSetHexColor(0xffffff);
	}else{
		ofSetHexColor(0x000000);
	}
	ofDrawBitmapString(title+": "+ofToString(color->x, 0)+
					   ", " + ofToString(color->y, 0) +
					   ", " + ofToString(color->z, 0), 12, height-4);
	ofPopMatrix();

}

<|endoftext|>


<commit_before>//
// CVector/instance_method_row.hpp
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
// Copyright (c) 2014 Rick Yang (rick68 at gmail dot com)
//
// This Source Code Form is subject to the terms of the Mozilla
// Public License v. 2.0. If a copy of the MPL was not distributed
// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
//

#ifndef EIGENJS_CVECTOR_INSTANCE_METHOD_ROW_HPP
#define EIGENJS_CVECTOR_INSTANCE_METHOD_ROW_HPP

namespace EigenJS {

EIGENJS_INSTANCE_METHOD(CVector, row,
{
  NanScope();

  if (args.Length() == 1 && args[0]->IsNumber()) {
    const T* const& obj = node::ObjectWrap::Unwrap<T>(args.This());
    const typename T::value_type& value = **obj;
    const typename T::value_type::Index& n = args[0]->Int32Value();

    if (T::is_out_of_range(value, n, 0)) {
      NanReturnUndefined();
    }

    v8::Local<v8::Value> argv[] = {
        args.This()             // CVector
      , args[0]                 // startRow
      , NanNew<v8::Integer>(1)  // blockRows
      };

    NanReturnValue(
      CVectorBlock::new_instance(
        args
      , sizeof(argv) / sizeof(v8::Local<v8::Value>)
      , argv
      )
    );
  }

  EIGENJS_THROW_ERROR_INVALID_ARGUMENT()
  NanReturnUndefined();
})

}  // namespace EigenJS

#endif  // EIGENJS_VECTOR_INSTANCE_METHOD_ROW_HPP
<commit_msg>src: improve readability<commit_after>//
// CVector/instance_method_row.hpp
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
// Copyright (c) 2014 Rick Yang (rick68 at gmail dot com)
//
// This Source Code Form is subject to the terms of the Mozilla
// Public License v. 2.0. If a copy of the MPL was not distributed
// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
//

#ifndef EIGENJS_CVECTOR_INSTANCE_METHOD_ROW_HPP
#define EIGENJS_CVECTOR_INSTANCE_METHOD_ROW_HPP

namespace EigenJS {

EIGENJS_INSTANCE_METHOD(CVector, row,
{
  NanScope();

  if (args.Length() == 1 && args[0]->IsNumber()) {
    const T* const& obj = node::ObjectWrap::Unwrap<T>(args.This());
    const typename T::value_type& value = **obj;
    const typename T::value_type::Index& n = args[0]->Int32Value();

    if (T::is_out_of_range(value, n, 0)) {
      NanReturnUndefined();
    }

    v8::Local<v8::Value> argv[] = {
        args.This()
      , args[0]                 // startRow
      , NanNew<v8::Integer>(1)  // blockRows
      };

    NanReturnValue(
      CVectorBlock::new_instance(
        args
      , sizeof(argv) / sizeof(v8::Local<v8::Value>)
      , argv
      )
    );
  }

  EIGENJS_THROW_ERROR_INVALID_ARGUMENT()
  NanReturnUndefined();
})

}  // namespace EigenJS

#endif  // EIGENJS_VECTOR_INSTANCE_METHOD_ROW_HPP
<|endoftext|>


<commit_before>#pragma once

#include <string>
#include <vector>
#include <map>
#include <set>

#include "acmacs-base/config.hh"

#pragma GCC diagnostic push
#include "acmacs-base/rapidjson-diagnostics.hh"
#include "rapidjson/writer.h"
#include "rapidjson/prettywriter.h"
#include "rapidjson/error/en.h"
#include "rapidjson/stringbuffer.h"
#pragma GCC diagnostic pop

#ifdef ACMACSD_FILESYSTEM
#include "acmacs-base/read-file.hh"
#endif
#include "acmacs-base/float.hh"
#include "acmacs-base/sfinae.hh"

// ----------------------------------------------------------------------
// Forward declarations
// ----------------------------------------------------------------------

namespace json_writer
{
    template <typename RW> class writer;
    class key;
}

template <typename RW, typename T> json_writer::writer<RW>& operator <<(json_writer::writer<RW>&, const std::vector<T>&);
template <typename RW> json_writer::writer<RW>& operator <<(json_writer::writer<RW>&, const char*);
template <typename RW> json_writer::writer<RW>& operator <<(json_writer::writer<RW>&, std::string);
template <typename RW> json_writer::writer<RW>& operator <<(json_writer::writer<RW>&, double);
template <typename RW> json_writer::writer<RW>& operator <<(json_writer::writer<RW>&, int);
template <typename RW> json_writer::writer<RW>& operator <<(json_writer::writer<RW>&, size_t);
template <typename RW> json_writer::writer<RW>& operator <<(json_writer::writer<RW>&, bool);
template <typename RW, typename Value> inline json_writer::writer<RW>& operator <<(json_writer::writer<RW>& aWriter, const std::vector<Value>& aList);
template <typename RW, typename Value> inline json_writer::writer<RW>& operator <<(json_writer::writer<RW>& aWriter, const std::set<Value>& aList);
template <typename RW, typename Value> json_writer::writer<RW>& operator <<(json_writer::writer<RW>& aWriter, const std::map<std::string, Value>& map_value);
template <typename RW> json_writer::writer<RW>& operator <<(json_writer::writer<RW>&, json_writer::key);

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

namespace json_writer
{
    template <typename RW> class writer : public RW // JsonWriterT
    {
     public:
        inline writer(std::string aKeyword) : RW(mBuffer), mKeyword(aKeyword) {}
        inline std::string to_string() const { return mBuffer.GetString(); }
        inline std::string keyword() const { return mKeyword; }

     private:
        rapidjson::StringBuffer mBuffer;
        std::string mKeyword;
    };

      // template <> inline writer<rapidjson::PrettyWriter<rapidjson::StringBuffer>>::writer(std::string aKeyword)
      //     : rapidjson::PrettyWriter<rapidjson::StringBuffer>(mBuffer), mKeyword(aKeyword)
      // {
      //     SetIndent(' ', 1);
      // }

      // ----------------------------------------------------------------------

    enum _StartArray { start_array };
    enum _EndArray { end_array };
    enum _StartObject { start_object };
    enum _EndObject { end_object };

    class key
    {
     public:
        inline key(const char* v) : value(v) {}
        inline key(std::string v) : value(v) {}
        inline key(char v) : value(1, v) {}
        inline operator const char*() const { return value.c_str(); }
        inline const char* as_char_ptr() const { return value.c_str(); }

     private:
        std::string value;
    };
}

// ----------------------------------------------------------------------

template <typename RW> inline json_writer::writer<RW>& operator <<(json_writer::writer<RW>& aWriter, json_writer::_StartArray) { aWriter.StartArray(); return aWriter; }
template <typename RW> inline json_writer::writer<RW>& operator <<(json_writer::writer<RW>& aWriter, json_writer::_EndArray) { aWriter.EndArray(); return aWriter; }
template <typename RW> inline json_writer::writer<RW>& operator <<(json_writer::writer<RW>& aWriter, json_writer::_StartObject) { aWriter.StartObject(); return aWriter; }
template <typename RW> inline json_writer::writer<RW>& operator <<(json_writer::writer<RW>& aWriter, json_writer::_EndObject) { aWriter.EndObject(); return aWriter; }

template <typename RW> inline json_writer::writer<RW>& operator <<(json_writer::writer<RW>& aWriter, json_writer::key value) { aWriter.Key(value.as_char_ptr()); return aWriter; }

// ----------------------------------------------------------------------
// ----------------------------------------------------------------------

namespace json_writer
{
    template <typename Key, typename Value> class _if_not_empty
    {
     public:
        inline _if_not_empty(Key&& aKey, Value&& aValue) : mKey(aKey), mValue(aValue) {}

        template <typename RW> friend inline writer<RW>& operator <<(writer<RW>& aWriter, const _if_not_empty<Key, Value>& data)
            {
                if (!data.mValue.empty())
                    aWriter << data.mKey << data.mValue;
                return aWriter;
            }

     private:
        Key mKey;
        Value mValue;
    };

    template <typename Key, typename Value> inline auto if_not_empty(Key&& aKey, Value&& aValue) { return _if_not_empty<Key, Value>(std::forward<Key>(aKey), std::forward<Value>(aValue)); }
    template <typename Value> inline auto if_not_empty(const char* aKey, Value&& aValue) { return _if_not_empty<key, Value>(key(aKey), std::forward<Value>(aValue)); }

// ----------------------------------------------------------------------

    template <typename Key, typename Value> class _if_non_negative
    {
     public:
        inline _if_non_negative(Key&& aKey, Value&& aValue) : mKey(aKey), mValue(aValue) {}

        template <typename RW> friend inline writer<RW>& operator <<(writer<RW>& aWriter, const _if_non_negative<Key, Value>& data)
            {
                if (data.mValue >= 0.0)
                    aWriter << data.mKey << data.mValue;
                return aWriter;
            }

     private:
        Key mKey;
        Value mValue;
    };

    template <typename Key, typename Value> inline auto if_non_negative(Key&& aKey, Value&& aValue) { return _if_non_negative<Key, Value>(std::forward<Key>(aKey), std::forward<Value>(aValue)); }
    template <typename Value> inline auto if_non_negative(const char* aKey, Value&& aValue) { return _if_non_negative<key, Value>(key(aKey), std::forward<Value>(aValue)); }

// ----------------------------------------------------------------------

    template <typename Key, typename Value> class _if_not
    {
     public:
        inline _if_not(Key&& aKey, Value&& aValue, bool aPred) : mKey(aKey), mValue(aValue), mPred(aPred) {}
        inline _if_not(Key&& aKey, Value aValue, bool aPred) : mKey(aKey), mValue(aValue), mPred(aPred) {}

        template <typename RW> friend inline writer<RW>& operator <<(writer<RW>& aWriter, const _if_not<Key, Value>& data)
            {
                if (!data.mPred)
                    aWriter << data.mKey << data.mValue;
                return aWriter;
            }

     private:
        Key mKey;
        Value mValue;
        bool mPred;
    };

    template <typename Key> inline auto if_not_zero(Key&& aKey, double aValue) { return _if_not<Key, double>(std::forward<Key>(aKey), aValue, float_zero(aValue)); }
    inline auto if_not_zero(const char* aKey, double aValue) { return _if_not<key, double>(key(aKey), aValue, float_zero(aValue)); }

    template <typename Key> inline auto if_not_one(Key&& aKey, double aValue) { return _if_not<Key, double>(std::forward<Key>(aKey), aValue, float_equal(aValue, 1.0)); }
    inline auto if_not_one(const char* aKey, double aValue) { return _if_not<key, double>(key(aKey), aValue, float_equal(aValue, 1.0)); }

    template <typename Key, typename Value> inline auto if_not_equal(Key&& aKey, Value&& aValue, Value aEq) { return _if_not<Key, Value>(std::forward<Key>(aKey), aValue, aValue == aEq); }
    template <typename Value> inline auto if_not_equal(const char* aKey, Value&& aValue, Value aEq) { return _if_not<key, Value>(key(aKey), aValue, aValue == aEq); }

    template <typename RW, typename List> inline writer<RW>& write_list(writer<RW>& aWriter, const List& aList)
    {
          // start_array and end_array do not work here due to namespace issue (in clang only?)
        aWriter.StartArray();
        for (const auto& e: aList)
            aWriter << e;
        aWriter.EndArray();
        return aWriter;
    }

    template <typename RW, typename List> inline writer<RW>& write_list(writer<RW>& aWriter, List&& aList)
    {
          // start_array and end_array do not work here due to namespace issue (in clang only?)
        aWriter.StartArray();
        for (auto& e: aList)
            aWriter << e;
        aWriter.EndArray();
        return aWriter;
    }

    template <typename RW, typename Object> inline writer<RW>& write_object(writer<RW>& aWriter, const Object& aObject)
    {
          // start_object and end_object do not work here due to namespace issue (in clang only?)
        aWriter.StartObject();
        for (const auto& e: aObject)
            aWriter << json_writer::key(e.first) << e.second;
        aWriter.EndObject();
        return aWriter;
    }

} // namespace json_writer

// ----------------------------------------------------------------------
// ----------------------------------------------------------------------

// to allow writer << func(writer, data) where func returns writer
template <typename RW> inline json_writer::writer<RW>& operator <<(json_writer::writer<RW>& aWriter, json_writer::writer<RW>&) { return aWriter; }

template <typename RW> inline json_writer::writer<RW>& operator <<(json_writer::writer<RW>& aWriter, const char* s) { aWriter.String(s, static_cast<unsigned>(strlen(s))); return aWriter; }
template <typename RW> inline json_writer::writer<RW>& operator <<(json_writer::writer<RW>& aWriter, std::string s) { aWriter.String(s.c_str(), static_cast<unsigned>(s.size())); return aWriter; }
template <typename RW> inline json_writer::writer<RW>& operator <<(json_writer::writer<RW>& aWriter, int value) { aWriter.Int(value); return aWriter; }
template <typename RW> inline json_writer::writer<RW>& operator <<(json_writer::writer<RW>& aWriter, size_t value) { aWriter.Uint64(value); return aWriter; }
template <typename RW> inline json_writer::writer<RW>& operator <<(json_writer::writer<RW>& aWriter, bool value) { aWriter.Bool(value); return aWriter; }
template <typename RW> inline json_writer::writer<RW>& operator <<(json_writer::writer<RW>& aWriter, double value) { aWriter.Double(value); return aWriter; }

template <typename RW, typename Value> inline json_writer::writer<RW>& operator <<(json_writer::writer<RW>& aWriter, const std::vector<Value>& aList)
{
    return json_writer::write_list(aWriter, aList);
}

template <typename RW, typename Value> inline json_writer::writer<RW>& operator <<(json_writer::writer<RW>& aWriter, const std::set<Value>& aList)
{
    return json_writer::write_list(aWriter, aList);
}

template <typename RW, typename Value> inline json_writer::writer<RW>& operator <<(json_writer::writer<RW>& aWriter, const std::map<std::string, Value>& map_value)
{
    return json_writer::write_object(aWriter, map_value);
}

template <typename RW, typename Key, typename std::enable_if<ad_sfinae::castable_to_char<Key>{}>::type* = nullptr>
                      inline json_writer::writer<RW>& operator <<(json_writer::writer<RW>& aWriter, Key aKey)
{
    const char k = static_cast<char>(aKey);
    aWriter.Key(&k, 1, false);
    return aWriter;
}

// ----------------------------------------------------------------------
// ----------------------------------------------------------------------

namespace json_writer
{
    enum _Finalize { finalize };

    class pretty;
}

std::string operator << (json_writer::pretty& aWriter, json_writer::_Finalize);

namespace json_writer
{
    inline void insert_emacs_indent_hint(std::string& aJson, size_t aIndent)
    {
        if (aJson.size() > 2 && aJson[0] == '{') {
            const std::string ind(aIndent - 1, ' ');
            aJson.insert(1, ind + "\"_\": \"-*- js-indent-level: " + std::to_string(aIndent) + " -*-\",");
        }
    }

    using compact = writer<rapidjson::Writer<rapidjson::StringBuffer>>;

    class pretty : public writer<rapidjson::PrettyWriter<rapidjson::StringBuffer>>
    {
     public:
        inline pretty(std::string keyword, size_t indent = 1) : writer<rapidjson::PrettyWriter<rapidjson::StringBuffer>>{keyword}, mIndent{indent}
            {
                SetIndent(' ', static_cast<unsigned int>(indent));
            }

        inline size_t indent() const { return mIndent; }
        inline operator std::string() { return *this << finalize; }

     private:
        size_t mIndent;

    }; // class pretty

} // namespace json_writer

// ----------------------------------------------------------------------

inline std::string operator << (json_writer::compact& aWriter, json_writer::_Finalize)
{
    return aWriter.to_string();
}

inline std::string operator << (json_writer::pretty& aWriter, json_writer::_Finalize)
{
    std::string result = aWriter.to_string();
    json_writer::insert_emacs_indent_hint(result, aWriter.indent());
    return result;
}

// ----------------------------------------------------------------------

namespace json_writer
{
    template <typename V> inline std::string pretty_json(const V& value, std::string keyword, size_t indent)
    {
        pretty aWriter{keyword, indent};
        aWriter << value;
        return aWriter << finalize;
    }

    template <typename V> inline std::string compact_json(const V& aValue, std::string keyword)
    {
        compact aWriter{keyword};
        aWriter << aValue;
        return aWriter << finalize;
    }

    template <typename V> inline std::string json(const V& value, std::string keyword, size_t indent)
    {
        if (indent)
            return pretty_json(value, keyword, indent);
        else
            return compact_json(value, keyword);
    }

      // ----------------------------------------------------------------------

#ifdef ACMACSD_FILESYSTEM
    using acmacs_base::ForceCompression;

    template <typename V> inline void export_to_json(const V& value, std::string keyword, std::string filename, size_t indent, ForceCompression force_compression = ForceCompression::No)
    {
        acmacs_base::write_file(filename, json(value, keyword, indent), force_compression);
    }
#endif

} // namespace json_writer

// ----------------------------------------------------------------------
/// Local Variables:
/// eval: (if (fboundp 'eu-rename-buffer) (eu-rename-buffer))
/// End:
<commit_msg>redundant keyword argument removed from json_writer<commit_after>#pragma once

#include <string>
#include <vector>
#include <map>
#include <set>

#include "acmacs-base/config.hh"

#pragma GCC diagnostic push
#include "acmacs-base/rapidjson-diagnostics.hh"
#include "rapidjson/writer.h"
#include "rapidjson/prettywriter.h"
#include "rapidjson/error/en.h"
#include "rapidjson/stringbuffer.h"
#pragma GCC diagnostic pop

#ifdef ACMACSD_FILESYSTEM
#include "acmacs-base/read-file.hh"
#endif
#include "acmacs-base/float.hh"
#include "acmacs-base/sfinae.hh"

// ----------------------------------------------------------------------
// Forward declarations
// ----------------------------------------------------------------------

namespace json_writer
{
    template <typename RW> class writer;
    class key;
}

template <typename RW, typename T> json_writer::writer<RW>& operator <<(json_writer::writer<RW>&, const std::vector<T>&);
template <typename RW> json_writer::writer<RW>& operator <<(json_writer::writer<RW>&, const char*);
template <typename RW> json_writer::writer<RW>& operator <<(json_writer::writer<RW>&, std::string);
template <typename RW> json_writer::writer<RW>& operator <<(json_writer::writer<RW>&, double);
template <typename RW> json_writer::writer<RW>& operator <<(json_writer::writer<RW>&, int);
template <typename RW> json_writer::writer<RW>& operator <<(json_writer::writer<RW>&, size_t);
template <typename RW> json_writer::writer<RW>& operator <<(json_writer::writer<RW>&, bool);
template <typename RW, typename Value> inline json_writer::writer<RW>& operator <<(json_writer::writer<RW>& aWriter, const std::vector<Value>& aList);
template <typename RW, typename Value> inline json_writer::writer<RW>& operator <<(json_writer::writer<RW>& aWriter, const std::set<Value>& aList);
template <typename RW, typename Value> json_writer::writer<RW>& operator <<(json_writer::writer<RW>& aWriter, const std::map<std::string, Value>& map_value);
template <typename RW> json_writer::writer<RW>& operator <<(json_writer::writer<RW>&, json_writer::key);

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

namespace json_writer
{
    template <typename RW> class writer : public RW // JsonWriterT
    {
     public:
        inline writer() : RW(mBuffer) {}
        inline std::string to_string() const { return mBuffer.GetString(); }

     private:
        rapidjson::StringBuffer mBuffer;
    };

      // ----------------------------------------------------------------------

    enum _StartArray { start_array };
    enum _EndArray { end_array };
    enum _StartObject { start_object };
    enum _EndObject { end_object };

    class key
    {
     public:
        inline key(const char* v) : value(v) {}
        inline key(std::string v) : value(v) {}
        inline key(char v) : value(1, v) {}
        inline operator const char*() const { return value.c_str(); }
        inline const char* as_char_ptr() const { return value.c_str(); }

     private:
        std::string value;
    };
}

// ----------------------------------------------------------------------

template <typename RW> inline json_writer::writer<RW>& operator <<(json_writer::writer<RW>& aWriter, json_writer::_StartArray) { aWriter.StartArray(); return aWriter; }
template <typename RW> inline json_writer::writer<RW>& operator <<(json_writer::writer<RW>& aWriter, json_writer::_EndArray) { aWriter.EndArray(); return aWriter; }
template <typename RW> inline json_writer::writer<RW>& operator <<(json_writer::writer<RW>& aWriter, json_writer::_StartObject) { aWriter.StartObject(); return aWriter; }
template <typename RW> inline json_writer::writer<RW>& operator <<(json_writer::writer<RW>& aWriter, json_writer::_EndObject) { aWriter.EndObject(); return aWriter; }

template <typename RW> inline json_writer::writer<RW>& operator <<(json_writer::writer<RW>& aWriter, json_writer::key value) { aWriter.Key(value.as_char_ptr()); return aWriter; }

// ----------------------------------------------------------------------
// ----------------------------------------------------------------------

namespace json_writer
{
    template <typename Key, typename Value> class _if_not_empty
    {
     public:
        inline _if_not_empty(Key&& aKey, Value&& aValue) : mKey(aKey), mValue(aValue) {}

        template <typename RW> friend inline writer<RW>& operator <<(writer<RW>& aWriter, const _if_not_empty<Key, Value>& data)
            {
                if (!data.mValue.empty())
                    aWriter << data.mKey << data.mValue;
                return aWriter;
            }

     private:
        Key mKey;
        Value mValue;
    };

    template <typename Key, typename Value> inline auto if_not_empty(Key&& aKey, Value&& aValue) { return _if_not_empty<Key, Value>(std::forward<Key>(aKey), std::forward<Value>(aValue)); }
    template <typename Value> inline auto if_not_empty(const char* aKey, Value&& aValue) { return _if_not_empty<key, Value>(key(aKey), std::forward<Value>(aValue)); }

// ----------------------------------------------------------------------

    template <typename Key, typename Value> class _if_non_negative
    {
     public:
        inline _if_non_negative(Key&& aKey, Value&& aValue) : mKey(aKey), mValue(aValue) {}

        template <typename RW> friend inline writer<RW>& operator <<(writer<RW>& aWriter, const _if_non_negative<Key, Value>& data)
            {
                if (data.mValue >= 0.0)
                    aWriter << data.mKey << data.mValue;
                return aWriter;
            }

     private:
        Key mKey;
        Value mValue;
    };

    template <typename Key, typename Value> inline auto if_non_negative(Key&& aKey, Value&& aValue) { return _if_non_negative<Key, Value>(std::forward<Key>(aKey), std::forward<Value>(aValue)); }
    template <typename Value> inline auto if_non_negative(const char* aKey, Value&& aValue) { return _if_non_negative<key, Value>(key(aKey), std::forward<Value>(aValue)); }

// ----------------------------------------------------------------------

    template <typename Key, typename Value> class _if_not
    {
     public:
        inline _if_not(Key&& aKey, Value&& aValue, bool aPred) : mKey(aKey), mValue(aValue), mPred(aPred) {}
        inline _if_not(Key&& aKey, Value aValue, bool aPred) : mKey(aKey), mValue(aValue), mPred(aPred) {}

        template <typename RW> friend inline writer<RW>& operator <<(writer<RW>& aWriter, const _if_not<Key, Value>& data)
            {
                if (!data.mPred)
                    aWriter << data.mKey << data.mValue;
                return aWriter;
            }

     private:
        Key mKey;
        Value mValue;
        bool mPred;
    };

    template <typename Key> inline auto if_not_zero(Key&& aKey, double aValue) { return _if_not<Key, double>(std::forward<Key>(aKey), aValue, float_zero(aValue)); }
    inline auto if_not_zero(const char* aKey, double aValue) { return _if_not<key, double>(key(aKey), aValue, float_zero(aValue)); }

    template <typename Key> inline auto if_not_one(Key&& aKey, double aValue) { return _if_not<Key, double>(std::forward<Key>(aKey), aValue, float_equal(aValue, 1.0)); }
    inline auto if_not_one(const char* aKey, double aValue) { return _if_not<key, double>(key(aKey), aValue, float_equal(aValue, 1.0)); }

    template <typename Key, typename Value> inline auto if_not_equal(Key&& aKey, Value&& aValue, Value aEq) { return _if_not<Key, Value>(std::forward<Key>(aKey), aValue, aValue == aEq); }
    template <typename Value> inline auto if_not_equal(const char* aKey, Value&& aValue, Value aEq) { return _if_not<key, Value>(key(aKey), aValue, aValue == aEq); }

    template <typename RW, typename List> inline writer<RW>& write_list(writer<RW>& aWriter, const List& aList)
    {
          // start_array and end_array do not work here due to namespace issue (in clang only?)
        aWriter.StartArray();
        for (const auto& e: aList)
            aWriter << e;
        aWriter.EndArray();
        return aWriter;
    }

    template <typename RW, typename List> inline writer<RW>& write_list(writer<RW>& aWriter, List&& aList)
    {
          // start_array and end_array do not work here due to namespace issue (in clang only?)
        aWriter.StartArray();
        for (auto& e: aList)
            aWriter << e;
        aWriter.EndArray();
        return aWriter;
    }

    template <typename RW, typename Object> inline writer<RW>& write_object(writer<RW>& aWriter, const Object& aObject)
    {
          // start_object and end_object do not work here due to namespace issue (in clang only?)
        aWriter.StartObject();
        for (const auto& e: aObject)
            aWriter << json_writer::key(e.first) << e.second;
        aWriter.EndObject();
        return aWriter;
    }

} // namespace json_writer

// ----------------------------------------------------------------------
// ----------------------------------------------------------------------

// to allow writer << func(writer, data) where func returns writer
template <typename RW> inline json_writer::writer<RW>& operator <<(json_writer::writer<RW>& aWriter, json_writer::writer<RW>&) { return aWriter; }

template <typename RW> inline json_writer::writer<RW>& operator <<(json_writer::writer<RW>& aWriter, const char* s) { aWriter.String(s, static_cast<unsigned>(strlen(s))); return aWriter; }
template <typename RW> inline json_writer::writer<RW>& operator <<(json_writer::writer<RW>& aWriter, std::string s) { aWriter.String(s.c_str(), static_cast<unsigned>(s.size())); return aWriter; }
template <typename RW> inline json_writer::writer<RW>& operator <<(json_writer::writer<RW>& aWriter, int value) { aWriter.Int(value); return aWriter; }
template <typename RW> inline json_writer::writer<RW>& operator <<(json_writer::writer<RW>& aWriter, size_t value) { aWriter.Uint64(value); return aWriter; }
template <typename RW> inline json_writer::writer<RW>& operator <<(json_writer::writer<RW>& aWriter, bool value) { aWriter.Bool(value); return aWriter; }
template <typename RW> inline json_writer::writer<RW>& operator <<(json_writer::writer<RW>& aWriter, double value) { aWriter.Double(value); return aWriter; }

template <typename RW, typename Value> inline json_writer::writer<RW>& operator <<(json_writer::writer<RW>& aWriter, const std::vector<Value>& aList)
{
    return json_writer::write_list(aWriter, aList);
}

template <typename RW, typename Value> inline json_writer::writer<RW>& operator <<(json_writer::writer<RW>& aWriter, const std::set<Value>& aList)
{
    return json_writer::write_list(aWriter, aList);
}

template <typename RW, typename Value> inline json_writer::writer<RW>& operator <<(json_writer::writer<RW>& aWriter, const std::map<std::string, Value>& map_value)
{
    return json_writer::write_object(aWriter, map_value);
}

template <typename RW, typename Key, typename std::enable_if<ad_sfinae::castable_to_char<Key>{}>::type* = nullptr>
                      inline json_writer::writer<RW>& operator <<(json_writer::writer<RW>& aWriter, Key aKey)
{
    const char k = static_cast<char>(aKey);
    aWriter.Key(&k, 1, false);
    return aWriter;
}

// ----------------------------------------------------------------------
// ----------------------------------------------------------------------

namespace json_writer
{
    enum _Finalize { finalize };

    class pretty;
}

std::string operator << (json_writer::pretty& aWriter, json_writer::_Finalize);

namespace json_writer
{
    inline void insert_emacs_indent_hint(std::string& aJson, size_t aIndent)
    {
        if (aJson.size() > 2 && aJson[0] == '{') {
            const std::string ind(aIndent - 1, ' ');
            aJson.insert(1, ind + "\"_\": \"-*- js-indent-level: " + std::to_string(aIndent) + " -*-\",");
        }
    }

    using compact = writer<rapidjson::Writer<rapidjson::StringBuffer>>;

    class pretty : public writer<rapidjson::PrettyWriter<rapidjson::StringBuffer>>
    {
     public:
        inline pretty(size_t indent = 1) : writer<rapidjson::PrettyWriter<rapidjson::StringBuffer>>{}, mIndent{indent}
            {
                SetIndent(' ', static_cast<unsigned int>(indent));
            }

        inline size_t indent() const { return mIndent; }
        inline operator std::string() { return *this << finalize; }

     private:
        size_t mIndent;

    }; // class pretty

} // namespace json_writer

// ----------------------------------------------------------------------

inline std::string operator << (json_writer::compact& aWriter, json_writer::_Finalize)
{
    return aWriter.to_string();
}

inline std::string operator << (json_writer::pretty& aWriter, json_writer::_Finalize)
{
    std::string result = aWriter.to_string();
    json_writer::insert_emacs_indent_hint(result, aWriter.indent());
    return result;
}

// ----------------------------------------------------------------------

namespace json_writer
{
    template <typename V> inline std::string pretty_json(const V& value, size_t indent)
    {
        pretty aWriter{indent};
        aWriter << value;
        return aWriter << finalize;
    }

    template <typename V> inline std::string compact_json(const V& aValue)
    {
        compact aWriter;
        aWriter << aValue;
        return aWriter << finalize;
    }

    template <typename V> inline std::string json(const V& value, size_t indent)
    {
        if (indent)
            return pretty_json(value, indent);
        else
            return compact_json(value);
    }

      // ----------------------------------------------------------------------

#ifdef ACMACSD_FILESYSTEM
    using acmacs_base::ForceCompression;

    template <typename V> inline void export_to_json(const V& value, std::string filename, size_t indent, ForceCompression force_compression = ForceCompression::No)
    {
        acmacs_base::write_file(filename, json(value, indent), force_compression);
    }
#endif

} // namespace json_writer

// ----------------------------------------------------------------------
/// Local Variables:
/// eval: (if (fboundp 'eu-rename-buffer) (eu-rename-buffer))
/// End:
<|endoftext|>


<commit_before>/*
 * Copyright 2014 Milian Wolff <mail@milianw.de>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Library 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.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 */

#include <cstdio>
#include <cstdlib>

#include <atomic>
#include <mutex>
#include <unordered_map>
#include <string>
#include <tuple>
#include <memory>

#include <boost/algorithm/string/replace.hpp>

#include <dlfcn.h>
#include <link.h>

#include "tracetree.h"

using namespace std;

namespace {

using malloc_t = void* (*) (size_t);
using free_t = void (*) (void*);
using realloc_t = void* (*) (void*, size_t);
using calloc_t = void* (*) (size_t, size_t);
using posix_memalign_t = int (*) (void **, size_t, size_t);
using valloc_t = void* (*) (size_t);
using aligned_alloc_t = void* (*) (size_t, size_t);
using dlopen_t = void* (*) (const char*, int);

malloc_t real_malloc = nullptr;
free_t real_free = nullptr;
realloc_t real_realloc = nullptr;
calloc_t real_calloc = nullptr;
posix_memalign_t real_posix_memalign = nullptr;
valloc_t real_valloc = nullptr;
aligned_alloc_t real_aligned_alloc = nullptr;
dlopen_t real_dlopen = nullptr;

// threadsafe stuff
atomic<bool> moduleCacheDirty(true);

struct HandleGuard
{
    HandleGuard()
        : wasLocked(inHandler)
    {
        inHandler = true;
    }

    ~HandleGuard()
    {
        inHandler = wasLocked;
    }

    const bool wasLocked;
    static thread_local bool inHandler;
};

thread_local bool HandleGuard::inHandler = false;

string env(const char* variable)
{
    const char* value = getenv(variable);
    return value ? string(value) : string();
}

struct Module
{
    string fileName;
    uintptr_t addressStart;
    uintptr_t addressEnd;

    bool operator<(const Module& module) const
    {
        return make_tuple(addressStart, addressEnd, fileName) < make_tuple(module.addressStart, module.addressEnd, module.fileName);
    }

    bool operator!=(const Module& module) const
    {
        return make_tuple(addressStart, addressEnd, fileName) != make_tuple(module.addressStart, module.addressEnd, module.fileName);
    }
};

struct Data
{
    Data()
    {
        modules.reserve(32);
        allocationInfo.reserve(16384);

        string outputFileName = env("DUMP_MALLOC_TRACE_OUTPUT");
        if (outputFileName.empty()) {
            // env var might not be set when linked directly into an executable
            outputFileName = "malloctrace.$$";
        } else if (outputFileName == "-" || outputFileName == "stdout") {
            out = stdout;
        } else if (outputFileName == "stderr") {
            out = stderr;
        }

        if (!out) {
            boost::replace_all(outputFileName, "$$", to_string(getpid()));
            out = fopen(outputFileName.c_str(), "w");
        }

        if (!out) {
            fprintf(stderr, "Failed to open output file: %s\n", outputFileName.c_str());
            exit(1);
        }

        // TODO: remember meta data about host application, such as cmdline, date of run, ...

        // cleanup environment to prevent tracing of child apps
        unsetenv("DUMP_MALLOC_TRACE_OUTPUT");
        unsetenv("LD_PRELOAD");
    }

    ~Data()
    {
        HandleGuard::inHandler = true;
        fclose(out);
    }

    void updateModuleCache()
    {
        // check list of loaded modules for unknown ones
        dl_iterate_phdr(dlopen_notify_callback, this);
        moduleCacheDirty = false;
    }

    /**
     * Mostly copied from vogl's src/libbacktrace/btrace.cpp
     */
    static int dlopen_notify_callback(struct dl_phdr_info *info, size_t /*size*/, void *_data)
    {
        auto data = reinterpret_cast<Data*>(_data);
        auto& modules = data->modules;
        bool isExe = false;
        const char *fileName = info->dlpi_name;
        const int BUF_SIZE = 1024;
        char buf[BUF_SIZE];
        // If we don't have a filename and we haven't added our main exe yet, do it now.
        if (!fileName || !fileName[0]) {
            if (modules.empty()) {
                isExe = true;
                ssize_t ret = readlink("/proc/self/exe", buf, sizeof(buf));
                if ((ret > 0) && (ret < (ssize_t)sizeof(buf))) {
                    buf[ret] = 0;
                    fileName = buf;
                }
            }
            if (!fileName || !fileName[0]) {
                return 0;
            }
        }

        uintptr_t addressStart = 0;
        uintptr_t addressEnd = 0;

        for (int i = 0; i < info->dlpi_phnum; i++) {
            if (info->dlpi_phdr[i].p_type == PT_LOAD) {
                if (addressEnd == 0) {
                    addressStart = info->dlpi_addr + info->dlpi_phdr[i].p_vaddr;
                    addressEnd = addressStart + info->dlpi_phdr[i].p_memsz;
                } else {
                    uintptr_t addr = info->dlpi_addr + info->dlpi_phdr[i].p_vaddr + info->dlpi_phdr[i].p_memsz;
                    if (addr > addressEnd) {
                        addressEnd = addr;
                    }
                }
            }
        }

        Module module{fileName, addressStart, addressEnd};

        auto it = lower_bound(modules.begin(), modules.end(), module);
        if (it == modules.end() || *it != module) {
            fprintf(data->out, "m %s %d %lx %lx\n", module.fileName.c_str(), isExe,
                                                    module.addressStart, module.addressEnd);
            modules.insert(it, module);
        }

        return 0;
    }

    void handleMalloc(void* ptr, size_t size)
    {
        Trace trace;
        if (!trace.fill()) {
            return;
        }

        lock_guard<mutex> lock(m_mutex);
        if (moduleCacheDirty) {
            updateModuleCache();
        }
        auto index = traceTree.index(trace, out);
        allocationInfo[ptr] = {size, index};
        fprintf(out, "+ %lu %lu\n", size, index);
    }

    void handleFree(void* ptr)
    {
        lock_guard<mutex> lock(m_mutex);
        auto it = allocationInfo.find(ptr);
        if (it == allocationInfo.end()) {
            return;
        }
        fprintf(out, "- %lu %lu\n", it->second.size, it->second.traceIndex);
        allocationInfo.erase(it);
    }

    mutex m_mutex;

    vector<Module> modules;
    TraceTree traceTree;
    struct AllocationInfo
    {
        size_t size;
        size_t traceIndex;
    };
    unordered_map<void*, AllocationInfo> allocationInfo;
    FILE* out = nullptr;
};

unique_ptr<Data> data;

template<typename T>
T findReal(const char* name)
{
    auto ret = dlsym(RTLD_NEXT, name);
    if (!ret) {
        fprintf(stderr, "Could not find original function %s\n", name);
        abort();
    }
    return reinterpret_cast<T>(ret);
}

/**
 * Dummy implementation, since the call to dlsym from findReal triggers a call to calloc.
 *
 * This is only called at startup and will eventually be replaced by the "proper" calloc implementation.
 */
void* dummy_calloc(size_t num, size_t size)
{
    const size_t MAX_SIZE = 1024;
    static char* buf[MAX_SIZE];
    static size_t offset = 0;
    if (!offset) {
        memset(buf, 0, MAX_SIZE);
    }
    size_t oldOffset = offset;
    offset += num * size;
    if (offset >= MAX_SIZE) {
        fprintf(stderr, "failed to initialize, dummy calloc buf size exhausted: %lu requested, %lu available\n", offset, MAX_SIZE);
        abort();
    }
    return buf + oldOffset;
}

void init()
{
    if (data || HandleGuard::inHandler) {
        fprintf(stderr, "initialization recursion detected\n");
        abort();
    }

    HandleGuard guard;

    real_calloc = &dummy_calloc;
    real_calloc = findReal<calloc_t>("calloc");
    real_dlopen = findReal<dlopen_t>("dlopen");
    real_malloc = findReal<malloc_t>("malloc");
    real_free = findReal<free_t>("free");
    real_realloc = findReal<realloc_t>("realloc");
    real_posix_memalign = findReal<posix_memalign_t>("posix_memalign");
    real_valloc = findReal<valloc_t>("valloc");
    real_aligned_alloc = findReal<aligned_alloc_t>("aligned_alloc");

    if (unw_set_caching_policy(unw_local_addr_space, UNW_CACHE_PER_THREAD)) {
        fprintf(stderr, "Failed to enable per-thread libunwind caching.\n");
    }

    data.reset(new Data);
}

}

extern "C" {

/// TODO: memalign, pvalloc, ...?

void* malloc(size_t size)
{
    if (!real_malloc) {
        init();
    }

    void* ret = real_malloc(size);

    if (ret && !HandleGuard::inHandler) {
        HandleGuard guard;
        data->handleMalloc(ret, size);
    }

    return ret;
}

void free(void* ptr)
{
    if (!real_free) {
        init();
    }

    real_free(ptr);

    if (ptr && !HandleGuard::inHandler) {
        HandleGuard guard;
        data->handleFree(ptr);
    }
}

void* realloc(void* ptr, size_t size)
{
    if (!real_realloc) {
        init();
    }

    void* ret = real_realloc(ptr, size);

    if (ret && !HandleGuard::inHandler) {
        HandleGuard guard;
        data->handleFree(ptr);
        data->handleMalloc(ret, size);
    }

    return ret;
}

void* calloc(size_t num, size_t size)
{
    if (!real_calloc) {
        init();
    }

    void* ret = real_calloc(num, size);

    if (ret && !HandleGuard::inHandler) {
        HandleGuard guard;
        data->handleMalloc(ret, num*size);
    }

    return ret;
}

int posix_memalign(void **memptr, size_t alignment, size_t size)
{
    if (!real_posix_memalign) {
        init();
    }

    int ret = real_posix_memalign(memptr, alignment, size);

    if (ret && !HandleGuard::inHandler) {
        HandleGuard guard;
        data->handleMalloc(*memptr, size);
    }

    return ret;
}

void* aligned_alloc(size_t alignment, size_t size)
{
    if (!real_aligned_alloc) {
        init();
    }

    void* ret = real_aligned_alloc(alignment, size);

    if (ret && !HandleGuard::inHandler) {
        HandleGuard guard;
        data->handleMalloc(ret, size);
    }

    return ret;
}

void* valloc(size_t size)
{
    if (!real_valloc) {
        init();
    }

    void* ret = real_valloc(size);

    if (ret && !HandleGuard::inHandler) {
        HandleGuard guard;
        data->handleMalloc(ret, size);
    }

    return ret;
}

void *dlopen(const char *filename, int flag)
{
    if (!real_dlopen) {
        init();
    }

    void* ret = real_dlopen(filename, flag);

    if (ret) {
        moduleCacheDirty = true;
    }

    return ret;
}

}
<commit_msg>realloc with nullptr is the same as pure malloc.<commit_after>/*
 * Copyright 2014 Milian Wolff <mail@milianw.de>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Library 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.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 */

#include <cstdio>
#include <cstdlib>

#include <atomic>
#include <mutex>
#include <unordered_map>
#include <string>
#include <tuple>
#include <memory>

#include <boost/algorithm/string/replace.hpp>

#include <dlfcn.h>
#include <link.h>

#include "tracetree.h"

using namespace std;

namespace {

using malloc_t = void* (*) (size_t);
using free_t = void (*) (void*);
using realloc_t = void* (*) (void*, size_t);
using calloc_t = void* (*) (size_t, size_t);
using posix_memalign_t = int (*) (void **, size_t, size_t);
using valloc_t = void* (*) (size_t);
using aligned_alloc_t = void* (*) (size_t, size_t);
using dlopen_t = void* (*) (const char*, int);

malloc_t real_malloc = nullptr;
free_t real_free = nullptr;
realloc_t real_realloc = nullptr;
calloc_t real_calloc = nullptr;
posix_memalign_t real_posix_memalign = nullptr;
valloc_t real_valloc = nullptr;
aligned_alloc_t real_aligned_alloc = nullptr;
dlopen_t real_dlopen = nullptr;

// threadsafe stuff
atomic<bool> moduleCacheDirty(true);

struct HandleGuard
{
    HandleGuard()
        : wasLocked(inHandler)
    {
        inHandler = true;
    }

    ~HandleGuard()
    {
        inHandler = wasLocked;
    }

    const bool wasLocked;
    static thread_local bool inHandler;
};

thread_local bool HandleGuard::inHandler = false;

string env(const char* variable)
{
    const char* value = getenv(variable);
    return value ? string(value) : string();
}

struct Module
{
    string fileName;
    uintptr_t addressStart;
    uintptr_t addressEnd;

    bool operator<(const Module& module) const
    {
        return make_tuple(addressStart, addressEnd, fileName) < make_tuple(module.addressStart, module.addressEnd, module.fileName);
    }

    bool operator!=(const Module& module) const
    {
        return make_tuple(addressStart, addressEnd, fileName) != make_tuple(module.addressStart, module.addressEnd, module.fileName);
    }
};

struct Data
{
    Data()
    {
        modules.reserve(32);
        allocationInfo.reserve(16384);

        string outputFileName = env("DUMP_MALLOC_TRACE_OUTPUT");
        if (outputFileName.empty()) {
            // env var might not be set when linked directly into an executable
            outputFileName = "malloctrace.$$";
        } else if (outputFileName == "-" || outputFileName == "stdout") {
            out = stdout;
        } else if (outputFileName == "stderr") {
            out = stderr;
        }

        if (!out) {
            boost::replace_all(outputFileName, "$$", to_string(getpid()));
            out = fopen(outputFileName.c_str(), "w");
        }

        if (!out) {
            fprintf(stderr, "Failed to open output file: %s\n", outputFileName.c_str());
            exit(1);
        }

        // TODO: remember meta data about host application, such as cmdline, date of run, ...

        // cleanup environment to prevent tracing of child apps
        unsetenv("DUMP_MALLOC_TRACE_OUTPUT");
        unsetenv("LD_PRELOAD");
    }

    ~Data()
    {
        HandleGuard::inHandler = true;
        fclose(out);
    }

    void updateModuleCache()
    {
        // check list of loaded modules for unknown ones
        dl_iterate_phdr(dlopen_notify_callback, this);
        moduleCacheDirty = false;
    }

    /**
     * Mostly copied from vogl's src/libbacktrace/btrace.cpp
     */
    static int dlopen_notify_callback(struct dl_phdr_info *info, size_t /*size*/, void *_data)
    {
        auto data = reinterpret_cast<Data*>(_data);
        auto& modules = data->modules;
        bool isExe = false;
        const char *fileName = info->dlpi_name;
        const int BUF_SIZE = 1024;
        char buf[BUF_SIZE];
        // If we don't have a filename and we haven't added our main exe yet, do it now.
        if (!fileName || !fileName[0]) {
            if (modules.empty()) {
                isExe = true;
                ssize_t ret = readlink("/proc/self/exe", buf, sizeof(buf));
                if ((ret > 0) && (ret < (ssize_t)sizeof(buf))) {
                    buf[ret] = 0;
                    fileName = buf;
                }
            }
            if (!fileName || !fileName[0]) {
                return 0;
            }
        }

        uintptr_t addressStart = 0;
        uintptr_t addressEnd = 0;

        for (int i = 0; i < info->dlpi_phnum; i++) {
            if (info->dlpi_phdr[i].p_type == PT_LOAD) {
                if (addressEnd == 0) {
                    addressStart = info->dlpi_addr + info->dlpi_phdr[i].p_vaddr;
                    addressEnd = addressStart + info->dlpi_phdr[i].p_memsz;
                } else {
                    uintptr_t addr = info->dlpi_addr + info->dlpi_phdr[i].p_vaddr + info->dlpi_phdr[i].p_memsz;
                    if (addr > addressEnd) {
                        addressEnd = addr;
                    }
                }
            }
        }

        Module module{fileName, addressStart, addressEnd};

        auto it = lower_bound(modules.begin(), modules.end(), module);
        if (it == modules.end() || *it != module) {
            fprintf(data->out, "m %s %d %lx %lx\n", module.fileName.c_str(), isExe,
                                                    module.addressStart, module.addressEnd);
            modules.insert(it, module);
        }

        return 0;
    }

    void handleMalloc(void* ptr, size_t size)
    {
        Trace trace;
        if (!trace.fill()) {
            return;
        }

        lock_guard<mutex> lock(m_mutex);
        if (moduleCacheDirty) {
            updateModuleCache();
        }
        auto index = traceTree.index(trace, out);
        allocationInfo[ptr] = {size, index};
        fprintf(out, "+ %lu %lu\n", size, index);
    }

    void handleFree(void* ptr)
    {
        lock_guard<mutex> lock(m_mutex);
        auto it = allocationInfo.find(ptr);
        if (it == allocationInfo.end()) {
            return;
        }
        fprintf(out, "- %lu %lu\n", it->second.size, it->second.traceIndex);
        allocationInfo.erase(it);
    }

    mutex m_mutex;

    vector<Module> modules;
    TraceTree traceTree;
    struct AllocationInfo
    {
        size_t size;
        size_t traceIndex;
    };
    unordered_map<void*, AllocationInfo> allocationInfo;
    FILE* out = nullptr;
};

unique_ptr<Data> data;

template<typename T>
T findReal(const char* name)
{
    auto ret = dlsym(RTLD_NEXT, name);
    if (!ret) {
        fprintf(stderr, "Could not find original function %s\n", name);
        abort();
    }
    return reinterpret_cast<T>(ret);
}

/**
 * Dummy implementation, since the call to dlsym from findReal triggers a call to calloc.
 *
 * This is only called at startup and will eventually be replaced by the "proper" calloc implementation.
 */
void* dummy_calloc(size_t num, size_t size)
{
    const size_t MAX_SIZE = 1024;
    static char* buf[MAX_SIZE];
    static size_t offset = 0;
    if (!offset) {
        memset(buf, 0, MAX_SIZE);
    }
    size_t oldOffset = offset;
    offset += num * size;
    if (offset >= MAX_SIZE) {
        fprintf(stderr, "failed to initialize, dummy calloc buf size exhausted: %lu requested, %lu available\n", offset, MAX_SIZE);
        abort();
    }
    return buf + oldOffset;
}

void init()
{
    if (data || HandleGuard::inHandler) {
        fprintf(stderr, "initialization recursion detected\n");
        abort();
    }

    HandleGuard guard;

    real_calloc = &dummy_calloc;
    real_calloc = findReal<calloc_t>("calloc");
    real_dlopen = findReal<dlopen_t>("dlopen");
    real_malloc = findReal<malloc_t>("malloc");
    real_free = findReal<free_t>("free");
    real_realloc = findReal<realloc_t>("realloc");
    real_posix_memalign = findReal<posix_memalign_t>("posix_memalign");
    real_valloc = findReal<valloc_t>("valloc");
    real_aligned_alloc = findReal<aligned_alloc_t>("aligned_alloc");

    if (unw_set_caching_policy(unw_local_addr_space, UNW_CACHE_PER_THREAD)) {
        fprintf(stderr, "Failed to enable per-thread libunwind caching.\n");
    }

    data.reset(new Data);
}

}

extern "C" {

/// TODO: memalign, pvalloc, ...?

void* malloc(size_t size)
{
    if (!real_malloc) {
        init();
    }

    void* ret = real_malloc(size);

    if (ret && !HandleGuard::inHandler) {
        HandleGuard guard;
        data->handleMalloc(ret, size);
    }

    return ret;
}

void free(void* ptr)
{
    if (!real_free) {
        init();
    }

    real_free(ptr);

    if (ptr && !HandleGuard::inHandler) {
        HandleGuard guard;
        data->handleFree(ptr);
    }
}

void* realloc(void* ptr, size_t size)
{
    if (!real_realloc) {
        init();
    }

    void* ret = real_realloc(ptr, size);

    if (ret && !HandleGuard::inHandler) {
        HandleGuard guard;
        if (ptr) {
            data->handleFree(ptr);
        }
        data->handleMalloc(ret, size);
    }

    return ret;
}

void* calloc(size_t num, size_t size)
{
    if (!real_calloc) {
        init();
    }

    void* ret = real_calloc(num, size);

    if (ret && !HandleGuard::inHandler) {
        HandleGuard guard;
        data->handleMalloc(ret, num*size);
    }

    return ret;
}

int posix_memalign(void **memptr, size_t alignment, size_t size)
{
    if (!real_posix_memalign) {
        init();
    }

    int ret = real_posix_memalign(memptr, alignment, size);

    if (ret && !HandleGuard::inHandler) {
        HandleGuard guard;
        data->handleMalloc(*memptr, size);
    }

    return ret;
}

void* aligned_alloc(size_t alignment, size_t size)
{
    if (!real_aligned_alloc) {
        init();
    }

    void* ret = real_aligned_alloc(alignment, size);

    if (ret && !HandleGuard::inHandler) {
        HandleGuard guard;
        data->handleMalloc(ret, size);
    }

    return ret;
}

void* valloc(size_t size)
{
    if (!real_valloc) {
        init();
    }

    void* ret = real_valloc(size);

    if (ret && !HandleGuard::inHandler) {
        HandleGuard guard;
        data->handleMalloc(ret, size);
    }

    return ret;
}

void *dlopen(const char *filename, int flag)
{
    if (!real_dlopen) {
        init();
    }

    void* ret = real_dlopen(filename, flag);

    if (ret) {
        moduleCacheDirty = true;
    }

    return ret;
}

}
<|endoftext|>