<commit_before>/*

Copyright (c) 2008, Arvid Norberg
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:

    * Redistributions of source code must retain the above copyright
      notice, this list of conditions and the following disclaimer.
    * Redistributions in binary form must reproduce the above copyright
      notice, this list of conditions and the following disclaimer in
      the documentation and/or other materials provided with the distribution.
    * Neither the name of the author nor the names of its
      contributors may be used to endorse or promote products derived
      from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.

*/

#include "libtorrent/lazy_entry.hpp"
#include <iostream>
#include <iomanip>
#include <cstring>

namespace libtorrent
{
	int fail_bdecode() { return -1; }

	// fills in 'val' with what the string between start and the
	// first occurance of the delimiter is interpreted as an int.
	// return the pointer to the delimiter, or 0 if there is a
	// parse error. val should be initialized to zero
	char const* parse_int(char const* start, char const* end, char delimiter, boost::int64_t& val)
	{
		while (start < end && *start != delimiter)
		{
			using namespace std;
			if (!isdigit(*start)) { fail_bdecode(); return 0; }
			val *= 10;
			val += *start - '0';
			++start;
		}
		return start;
	}

	char const* find_char(char const* start, char const* end, char delimiter)
	{
		while (start < end && *start != delimiter) ++start;
		return start;
	}

	// return 0 = success
	int lazy_bdecode(char const* start, char const* end, lazy_entry& ret, int depth_limit)
	{
		ret.clear();
		if (start == end) return 0;

		std::vector<lazy_entry*> stack;

		stack.push_back(&ret);
		while (start < end)
		{
			if (stack.empty()) break; // done!

			lazy_entry* top = stack.back();

			if (int(stack.size()) > depth_limit) return fail_bdecode();
			if (start == end) return fail_bdecode();
			char t = *start;
			++start;
			if (start == end && t != 'e') return fail_bdecode();

			switch (top->type())
			{
				case lazy_entry::dict_t:
				{
					if (t == 'e')
					{
						top->set_end(start);
						stack.pop_back();
						continue;
					}
					boost::int64_t len = t - '0';
					start = parse_int(start, end, ':', len);
					if (start == 0 || start + len + 3 > end || *start != ':') return fail_bdecode();
					++start;
					lazy_entry* ent = top->dict_append(start);
					start += len;
					stack.push_back(ent);
					t = *start;
					++start;
					break;
				}
				case lazy_entry::list_t:
				{
					if (t == 'e')
					{
						top->set_end(start);
						stack.pop_back();
						continue;
					}
					lazy_entry* ent = top->list_append();
					stack.push_back(ent);
					break;
				}
				default: break;
			}

			top = stack.back();
			switch (t)
			{
				case 'd':
					top->construct_dict(start - 1);
					continue;
				case 'l':
					top->construct_list(start - 1);
					continue;
				case 'i':
				{
					char const* int_start = start;
					start = find_char(start, end, 'e');
					top->construct_int(int_start, start - int_start);
					if (start == end) return fail_bdecode();
					TORRENT_ASSERT(*start == 'e');
					++start;
					stack.pop_back();
					continue;
				}
				default:
				{
					using namespace std;
					if (!isdigit(t)) return fail_bdecode();

					boost::int64_t len = t - '0';
					start = parse_int(start, end, ':', len);
					if (start == 0 || start + len + 1 > end || *start != ':') return fail_bdecode();
					++start;
					top->construct_string(start, int(len));
					stack.pop_back();
					start += len;
					continue;
				}
			}
			return 0;
		}
		return 0;
	}

	size_type lazy_entry::int_value() const
	{
		TORRENT_ASSERT(m_type == int_t);
		boost::int64_t val = 0;
		bool negative = false;
		if (*m_data.start == '-') negative = true;
		parse_int(negative?m_data.start+1:m_data.start, m_data.start + m_size, 'e', val);
		if (negative) val = -val;
		return val;
	}

	lazy_entry* lazy_entry::dict_append(char const* name)
	{
		TORRENT_ASSERT(m_type == dict_t);
		TORRENT_ASSERT(m_size <= m_capacity);
		if (m_capacity == 0)
		{
			int capacity = 10;
			m_data.dict = new (std::nothrow) std::pair<char const*, lazy_entry>[capacity];
			if (m_data.dict == 0) return 0;
			m_capacity = capacity;
		}
		else if (m_size == m_capacity)
		{
			int capacity = m_capacity * 2;
			std::pair<char const*, lazy_entry>* tmp = new (std::nothrow) std::pair<char const*, lazy_entry>[capacity];
			if (tmp == 0) return 0;
			std::memcpy(tmp, m_data.dict, sizeof(std::pair<char const*, lazy_entry>) * m_size);
			delete[] m_data.dict;
			m_data.dict = tmp;
			m_capacity = capacity;
		}

		TORRENT_ASSERT(m_size < m_capacity);
		std::pair<char const*, lazy_entry>& ret = m_data.dict[m_size++];
		ret.first = name;
		return &ret.second;
	}

	namespace
	{
		// the number of decimal digits needed
		// to represent the given value
		int num_digits(int val)
		{
			int ret = 1;
			while (val > 10)
			{
				++ret;
				val /= 10;
			}
			return ret;
		}
	}

	void lazy_entry::construct_string(char const* start, int length)
	{
		TORRENT_ASSERT(m_type == none_t);
		m_type = string_t;
		m_data.start = start;
		m_size = length;
		m_begin = start - 1 - num_digits(length);
		m_end = start + length;
	}

	namespace
	{
		// str1 is null-terminated
		// str2 is not, str2 is len2 chars
		bool string_equal(char const* str1, char const* str2, int len2)
		{
			while (len2 > 0)
			{
				if (*str1 != *str2) return false;
				if (*str1 == 0) return false;
				++str1;
				++str2;
				--len2;
			}
			return *str1 == 0;
		}
	}

	std::string lazy_entry::dict_find_string_value(char const* name) const
	{
		lazy_entry const* e = dict_find(name);
		if (e == 0 || e->type() != lazy_entry::string_t) return std::string();
		return e->string_value();
	}

	size_type lazy_entry::dict_find_int_value(char const* name, size_type default_val) const
	{
		lazy_entry const* e = dict_find(name);
		if (e == 0 || e->type() != lazy_entry::int_t) return default_val;
		return e->int_value();
	}

	lazy_entry const* lazy_entry::dict_find_dict(char const* name) const
	{
		lazy_entry const* e = dict_find(name);
		if (e == 0 || e->type() != lazy_entry::dict_t) return 0;
		return e;
	}

	lazy_entry const* lazy_entry::dict_find_list(char const* name) const
	{
		lazy_entry const* e = dict_find(name);
		if (e == 0 || e->type() != lazy_entry::list_t) return 0;
		return e;
	}

	lazy_entry* lazy_entry::dict_find(char const* name)
	{
		TORRENT_ASSERT(m_type == dict_t);
		for (int i = 0; i < m_size; ++i)
		{
			std::pair<char const*, lazy_entry> const& e = m_data.dict[i];
			if (string_equal(name, e.first, e.second.m_begin - e.first))
				return &m_data.dict[i].second;
		}
		return 0;
	}

	lazy_entry* lazy_entry::list_append()
	{
		TORRENT_ASSERT(m_type == list_t);
		TORRENT_ASSERT(m_size <= m_capacity);
		if (m_capacity == 0)
		{
			int capacity = 10;
			m_data.list = new (std::nothrow) lazy_entry[capacity];
			if (m_data.list == 0) return 0;
			m_capacity = capacity;
		}
		else if (m_size == m_capacity)
		{
			int capacity = m_capacity * 2;
			lazy_entry* tmp = new (std::nothrow) lazy_entry[capacity];
			if (tmp == 0) return 0;
			std::memcpy(tmp, m_data.list, sizeof(lazy_entry) * m_size);
			delete[] m_data.list;
			m_data.list = tmp;
			m_capacity = capacity;
		}

		TORRENT_ASSERT(m_size < m_capacity);
		return m_data.list + (m_size++);
	}

	std::string lazy_entry::list_string_value_at(int i) const
	{
		lazy_entry const* e = list_at(i);
		if (e == 0 || e->type() != lazy_entry::string_t) return std::string();
		return e->string_value();
	}

	size_type lazy_entry::list_int_value_at(int i, size_type default_val) const
	{
		lazy_entry const* e = list_at(i);
		if (e == 0 || e->type() != lazy_entry::int_t) return default_val;
		return e->int_value();
	}

	void lazy_entry::clear()
	{
		switch (m_type)
		{
			case list_t: delete[] m_data.list; break;
			case dict_t: delete[] m_data.dict; break;
			default: break;
		}
		m_size = 0;
		m_capacity = 0;
		m_type = none_t;
	}

	std::pair<char const*, int> lazy_entry::data_section() const
	{
		typedef std::pair<char const*, int> return_t;
		return return_t(m_begin, m_end - m_begin);
	}

	std::ostream& operator<<(std::ostream& os, lazy_entry const& e)
	{
		switch (e.type())
		{
			case lazy_entry::none_t: return os << "none";
			case lazy_entry::int_t: return os << e.int_value();
			case lazy_entry::string_t:
			{
				bool printable = true;
				char const* str = e.string_ptr();
				for (int i = 0; i < e.string_length(); ++i)
				{
					using namespace std;
					if (isprint(str[i])) continue;
					printable = false;
					break;
				}
				os << "'";
				if (printable) return os << e.string_value() << "'";
				for (int i = 0; i < e.string_length(); ++i)
					os << std::hex << int((unsigned char)(str[i]));
				return os << "'";
			}
			case lazy_entry::list_t:
			{
				os << "[";
				bool one_liner = (e.list_size() == 0
					|| e.list_at(0)->type() == lazy_entry::int_t
					|| (e.list_at(0)->type() == lazy_entry::string_t
						&& e.list_at(0)->string_length() < 10))
					&& e.list_size() < 5;
				if (!one_liner) os << "\n";
				for (int i = 0; i < e.list_size(); ++i)
				{
					if (i == 0 && one_liner) os << " ";
					os << *e.list_at(i);
					if (i < e.list_size() - 1) os << (one_liner?", ":",\n");
					else os << (one_liner?" ":"\n");
				}
				return os << "]";
			}
			case lazy_entry::dict_t:
			{
				os << "{";
				bool one_liner = (e.dict_size() == 0
					|| e.dict_at(0).second->type() == lazy_entry::int_t
					|| (e.dict_at(0).second->type() == lazy_entry::string_t
						&& e.dict_at(0).second->string_length() < 10)
					|| e.dict_at(0).first.size() < 10)
					&& e.dict_size() < 5;

				if (!one_liner) os << "\n";
				for (int i = 0; i < e.dict_size(); ++i)
				{
					if (i == 0 && one_liner) os << " ";
					std::pair<std::string, lazy_entry const*> ent = e.dict_at(i);
					os << "'" << ent.first << "': " << *ent.second;
					if (i < e.dict_size() - 1) os << (one_liner?", ":",\n");
					else os << (one_liner?" ":"\n");
				}
				return os << "}";
			}
		}
		return os;
	}

};

<commit_msg>bdecode print formatting fix<commit_after>/*

Copyright (c) 2008, Arvid Norberg
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:

    * Redistributions of source code must retain the above copyright
      notice, this list of conditions and the following disclaimer.
    * Redistributions in binary form must reproduce the above copyright
      notice, this list of conditions and the following disclaimer in
      the documentation and/or other materials provided with the distribution.
    * Neither the name of the author nor the names of its
      contributors may be used to endorse or promote products derived
      from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.

*/

#include "libtorrent/lazy_entry.hpp"
#include <iostream>
#include <iomanip>
#include <cstring>

namespace libtorrent
{
	int fail_bdecode() { return -1; }

	// fills in 'val' with what the string between start and the
	// first occurance of the delimiter is interpreted as an int.
	// return the pointer to the delimiter, or 0 if there is a
	// parse error. val should be initialized to zero
	char const* parse_int(char const* start, char const* end, char delimiter, boost::int64_t& val)
	{
		while (start < end && *start != delimiter)
		{
			using namespace std;
			if (!isdigit(*start)) { fail_bdecode(); return 0; }
			val *= 10;
			val += *start - '0';
			++start;
		}
		return start;
	}

	char const* find_char(char const* start, char const* end, char delimiter)
	{
		while (start < end && *start != delimiter) ++start;
		return start;
	}

	// return 0 = success
	int lazy_bdecode(char const* start, char const* end, lazy_entry& ret, int depth_limit)
	{
		ret.clear();
		if (start == end) return 0;

		std::vector<lazy_entry*> stack;

		stack.push_back(&ret);
		while (start < end)
		{
			if (stack.empty()) break; // done!

			lazy_entry* top = stack.back();

			if (int(stack.size()) > depth_limit) return fail_bdecode();
			if (start == end) return fail_bdecode();
			char t = *start;
			++start;
			if (start == end && t != 'e') return fail_bdecode();

			switch (top->type())
			{
				case lazy_entry::dict_t:
				{
					if (t == 'e')
					{
						top->set_end(start);
						stack.pop_back();
						continue;
					}
					boost::int64_t len = t - '0';
					start = parse_int(start, end, ':', len);
					if (start == 0 || start + len + 3 > end || *start != ':') return fail_bdecode();
					++start;
					lazy_entry* ent = top->dict_append(start);
					start += len;
					stack.push_back(ent);
					t = *start;
					++start;
					break;
				}
				case lazy_entry::list_t:
				{
					if (t == 'e')
					{
						top->set_end(start);
						stack.pop_back();
						continue;
					}
					lazy_entry* ent = top->list_append();
					stack.push_back(ent);
					break;
				}
				default: break;
			}

			top = stack.back();
			switch (t)
			{
				case 'd':
					top->construct_dict(start - 1);
					continue;
				case 'l':
					top->construct_list(start - 1);
					continue;
				case 'i':
				{
					char const* int_start = start;
					start = find_char(start, end, 'e');
					top->construct_int(int_start, start - int_start);
					if (start == end) return fail_bdecode();
					TORRENT_ASSERT(*start == 'e');
					++start;
					stack.pop_back();
					continue;
				}
				default:
				{
					using namespace std;
					if (!isdigit(t)) return fail_bdecode();

					boost::int64_t len = t - '0';
					start = parse_int(start, end, ':', len);
					if (start == 0 || start + len + 1 > end || *start != ':') return fail_bdecode();
					++start;
					top->construct_string(start, int(len));
					stack.pop_back();
					start += len;
					continue;
				}
			}
			return 0;
		}
		return 0;
	}

	size_type lazy_entry::int_value() const
	{
		TORRENT_ASSERT(m_type == int_t);
		boost::int64_t val = 0;
		bool negative = false;
		if (*m_data.start == '-') negative = true;
		parse_int(negative?m_data.start+1:m_data.start, m_data.start + m_size, 'e', val);
		if (negative) val = -val;
		return val;
	}

	lazy_entry* lazy_entry::dict_append(char const* name)
	{
		TORRENT_ASSERT(m_type == dict_t);
		TORRENT_ASSERT(m_size <= m_capacity);
		if (m_capacity == 0)
		{
			int capacity = 10;
			m_data.dict = new (std::nothrow) std::pair<char const*, lazy_entry>[capacity];
			if (m_data.dict == 0) return 0;
			m_capacity = capacity;
		}
		else if (m_size == m_capacity)
		{
			int capacity = m_capacity * 2;
			std::pair<char const*, lazy_entry>* tmp = new (std::nothrow) std::pair<char const*, lazy_entry>[capacity];
			if (tmp == 0) return 0;
			std::memcpy(tmp, m_data.dict, sizeof(std::pair<char const*, lazy_entry>) * m_size);
			delete[] m_data.dict;
			m_data.dict = tmp;
			m_capacity = capacity;
		}

		TORRENT_ASSERT(m_size < m_capacity);
		std::pair<char const*, lazy_entry>& ret = m_data.dict[m_size++];
		ret.first = name;
		return &ret.second;
	}

	namespace
	{
		// the number of decimal digits needed
		// to represent the given value
		int num_digits(int val)
		{
			int ret = 1;
			while (val > 10)
			{
				++ret;
				val /= 10;
			}
			return ret;
		}
	}

	void lazy_entry::construct_string(char const* start, int length)
	{
		TORRENT_ASSERT(m_type == none_t);
		m_type = string_t;
		m_data.start = start;
		m_size = length;
		m_begin = start - 1 - num_digits(length);
		m_end = start + length;
	}

	namespace
	{
		// str1 is null-terminated
		// str2 is not, str2 is len2 chars
		bool string_equal(char const* str1, char const* str2, int len2)
		{
			while (len2 > 0)
			{
				if (*str1 != *str2) return false;
				if (*str1 == 0) return false;
				++str1;
				++str2;
				--len2;
			}
			return *str1 == 0;
		}
	}

	std::string lazy_entry::dict_find_string_value(char const* name) const
	{
		lazy_entry const* e = dict_find(name);
		if (e == 0 || e->type() != lazy_entry::string_t) return std::string();
		return e->string_value();
	}

	size_type lazy_entry::dict_find_int_value(char const* name, size_type default_val) const
	{
		lazy_entry const* e = dict_find(name);
		if (e == 0 || e->type() != lazy_entry::int_t) return default_val;
		return e->int_value();
	}

	lazy_entry const* lazy_entry::dict_find_dict(char const* name) const
	{
		lazy_entry const* e = dict_find(name);
		if (e == 0 || e->type() != lazy_entry::dict_t) return 0;
		return e;
	}

	lazy_entry const* lazy_entry::dict_find_list(char const* name) const
	{
		lazy_entry const* e = dict_find(name);
		if (e == 0 || e->type() != lazy_entry::list_t) return 0;
		return e;
	}

	lazy_entry* lazy_entry::dict_find(char const* name)
	{
		TORRENT_ASSERT(m_type == dict_t);
		for (int i = 0; i < m_size; ++i)
		{
			std::pair<char const*, lazy_entry> const& e = m_data.dict[i];
			if (string_equal(name, e.first, e.second.m_begin - e.first))
				return &m_data.dict[i].second;
		}
		return 0;
	}

	lazy_entry* lazy_entry::list_append()
	{
		TORRENT_ASSERT(m_type == list_t);
		TORRENT_ASSERT(m_size <= m_capacity);
		if (m_capacity == 0)
		{
			int capacity = 10;
			m_data.list = new (std::nothrow) lazy_entry[capacity];
			if (m_data.list == 0) return 0;
			m_capacity = capacity;
		}
		else if (m_size == m_capacity)
		{
			int capacity = m_capacity * 2;
			lazy_entry* tmp = new (std::nothrow) lazy_entry[capacity];
			if (tmp == 0) return 0;
			std::memcpy(tmp, m_data.list, sizeof(lazy_entry) * m_size);
			delete[] m_data.list;
			m_data.list = tmp;
			m_capacity = capacity;
		}

		TORRENT_ASSERT(m_size < m_capacity);
		return m_data.list + (m_size++);
	}

	std::string lazy_entry::list_string_value_at(int i) const
	{
		lazy_entry const* e = list_at(i);
		if (e == 0 || e->type() != lazy_entry::string_t) return std::string();
		return e->string_value();
	}

	size_type lazy_entry::list_int_value_at(int i, size_type default_val) const
	{
		lazy_entry const* e = list_at(i);
		if (e == 0 || e->type() != lazy_entry::int_t) return default_val;
		return e->int_value();
	}

	void lazy_entry::clear()
	{
		switch (m_type)
		{
			case list_t: delete[] m_data.list; break;
			case dict_t: delete[] m_data.dict; break;
			default: break;
		}
		m_size = 0;
		m_capacity = 0;
		m_type = none_t;
	}

	std::pair<char const*, int> lazy_entry::data_section() const
	{
		typedef std::pair<char const*, int> return_t;
		return return_t(m_begin, m_end - m_begin);
	}

	std::ostream& operator<<(std::ostream& os, lazy_entry const& e)
	{
		switch (e.type())
		{
			case lazy_entry::none_t: return os << "none";
			case lazy_entry::int_t: return os << e.int_value();
			case lazy_entry::string_t:
			{
				bool printable = true;
				char const* str = e.string_ptr();
				for (int i = 0; i < e.string_length(); ++i)
				{
					using namespace std;
					if (isprint(str[i])) continue;
					printable = false;
					break;
				}
				os << "'";
				if (printable) return os << e.string_value() << "'";
				for (int i = 0; i < e.string_length(); ++i)
					os << std::hex << int((unsigned char)(str[i]));
				return os << "'";
			}
			case lazy_entry::list_t:
			{
				os << "[";
				bool one_liner = (e.list_size() == 0
					|| e.list_at(0)->type() == lazy_entry::int_t
					|| (e.list_at(0)->type() == lazy_entry::string_t
						&& (e.list_at(0)->string_length() < 10
							|| e.list_size() < 2)))
					&& e.list_size() < 5;
				if (!one_liner) os << "\n";
				for (int i = 0; i < e.list_size(); ++i)
				{
					if (i == 0 && one_liner) os << " ";
					os << *e.list_at(i);
					if (i < e.list_size() - 1) os << (one_liner?", ":",\n");
					else os << (one_liner?" ":"\n");
				}
				return os << "]";
			}
			case lazy_entry::dict_t:
			{
				os << "{";
				bool one_liner = (e.dict_size() == 0
					|| e.dict_at(0).second->type() == lazy_entry::int_t
					|| (e.dict_at(0).second->type() == lazy_entry::string_t
						&& e.dict_at(0).second->string_length() < 30)
					|| e.dict_at(0).first.size() < 10)
					&& e.dict_size() < 5;

				if (!one_liner) os << "\n";
				for (int i = 0; i < e.dict_size(); ++i)
				{
					if (i == 0 && one_liner) os << " ";
					std::pair<std::string, lazy_entry const*> ent = e.dict_at(i);
					os << "'" << ent.first << "': " << *ent.second;
					if (i < e.dict_size() - 1) os << (one_liner?", ":",\n");
					else os << (one_liner?" ":"\n");
				}
				return os << "}";
			}
		}
		return os;
	}

};

<|endoftext|>


<commit_before>//===-- asan_stats.cpp ----------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// This file is a part of AddressSanitizer, an address sanity checker.
//
// Code related to statistics collected by AddressSanitizer.
//===----------------------------------------------------------------------===//
#include "asan_allocator.h"
#include "asan_interceptors.h"
#include "asan_internal.h"
#include "asan_stats.h"
#include "asan_thread.h"
#include "sanitizer_common/sanitizer_allocator_interface.h"
#include "sanitizer_common/sanitizer_mutex.h"
#include "sanitizer_common/sanitizer_stackdepot.h"

namespace __asan {

AsanStats::AsanStats() {
  Clear();
}

void AsanStats::Clear() {
  CHECK(REAL(memset));
  REAL(memset)(this, 0, sizeof(AsanStats));
}

static void PrintMallocStatsArray(const char *prefix,
                                  uptr *array, uptr size) {
  Printf("%s", prefix);
  for (uptr i = 0; i < size; i++) {
    if (!array[i]) continue;
    Printf("%zu:%zu; ", i, array[i]);
  }
  Printf("\n");
}

void AsanStats::Print() {
  Printf("Stats: %zuM malloced (%zuM for red zones) by %zu calls\n",
             malloced>>20, malloced_redzones>>20, mallocs);
  Printf("Stats: %zuM realloced by %zu calls\n", realloced>>20, reallocs);
  Printf("Stats: %zuM freed by %zu calls\n", freed>>20, frees);
  Printf("Stats: %zuM really freed by %zu calls\n",
             really_freed>>20, real_frees);
  Printf("Stats: %zuM (%zuM-%zuM) mmaped; %zu maps, %zu unmaps\n",
             (mmaped-munmaped)>>20, mmaped>>20, munmaped>>20,
             mmaps, munmaps);

  PrintMallocStatsArray("  mallocs by size class: ", malloced_by_size,
                        get_allocator().KMaxSize());
  Printf("Stats: malloc large: %zu\n", malloc_large);
}

void AsanStats::MergeFrom(const AsanStats *stats) {
  uptr *dst_ptr = reinterpret_cast<uptr*>(this);
  const uptr *src_ptr = reinterpret_cast<const uptr*>(stats);
  uptr num_fields = sizeof(*this) / sizeof(uptr);
  for (uptr i = 0; i < num_fields; i++)
    dst_ptr[i] += src_ptr[i];
}

static BlockingMutex print_lock(LINKER_INITIALIZED);

static AsanStats unknown_thread_stats(LINKER_INITIALIZED);
static AsanStats dead_threads_stats(LINKER_INITIALIZED);
static BlockingMutex dead_threads_stats_lock(LINKER_INITIALIZED);
// Required for malloc_zone_statistics() on OS X. This can't be stored in
// per-thread AsanStats.
static uptr max_malloced_memory;

static void MergeThreadStats(ThreadContextBase *tctx_base, void *arg) {
  AsanStats *accumulated_stats = reinterpret_cast<AsanStats*>(arg);
  AsanThreadContext *tctx = static_cast<AsanThreadContext*>(tctx_base);
  if (AsanThread *t = tctx->thread)
    accumulated_stats->MergeFrom(&t->stats());
}

static void GetAccumulatedStats(AsanStats *stats) {
  stats->Clear();
  {
    ThreadRegistryLock l(&asanThreadRegistry());
    asanThreadRegistry()
        .RunCallbackForEachThreadLocked(MergeThreadStats, stats);
  }
  stats->MergeFrom(&unknown_thread_stats);
  {
    BlockingMutexLock lock(&dead_threads_stats_lock);
    stats->MergeFrom(&dead_threads_stats);
  }
  // This is not very accurate: we may miss allocation peaks that happen
  // between two updates of accumulated_stats_. For more accurate bookkeeping
  // the maximum should be updated on every malloc(), which is unacceptable.
  if (max_malloced_memory < stats->malloced) {
    max_malloced_memory = stats->malloced;
  }
}

void FlushToDeadThreadStats(AsanStats *stats) {
  BlockingMutexLock lock(&dead_threads_stats_lock);
  dead_threads_stats.MergeFrom(stats);
  stats->Clear();
}

void FillMallocStatistics(AsanMallocStats *malloc_stats) {
  AsanStats stats;
  GetAccumulatedStats(&stats);
  malloc_stats->blocks_in_use = stats.mallocs;
  malloc_stats->size_in_use = stats.malloced;
  malloc_stats->max_size_in_use = max_malloced_memory;
  malloc_stats->size_allocated = stats.mmaped;
}

AsanStats &GetCurrentThreadStats() {
  AsanThread *t = GetCurrentThread();
  return (t) ? t->stats() : unknown_thread_stats;
}

static void PrintAccumulatedStats() {
  AsanStats stats;
  GetAccumulatedStats(&stats);
  // Use lock to keep reports from mixing up.
  BlockingMutexLock lock(&print_lock);
  stats.Print();
  StackDepotStats *stack_depot_stats = StackDepotGetStats();
  Printf("Stats: StackDepot: %zd ids; %zdM allocated\n",
         stack_depot_stats->n_uniq_ids, stack_depot_stats->allocated >> 20);
  PrintInternalAllocatorStats();
}

}  // namespace __asan

// ---------------------- Interface ---------------- {{{1
using namespace __asan;  // NOLINT

uptr __sanitizer_get_current_allocated_bytes() {
  AsanStats stats;
  GetAccumulatedStats(&stats);
  uptr malloced = stats.malloced;
  uptr freed = stats.freed;
  // Return sane value if malloced < freed due to racy
  // way we update accumulated stats.
  return (malloced > freed) ? malloced - freed : 1;
}

uptr __sanitizer_get_heap_size() {
  AsanStats stats;
  GetAccumulatedStats(&stats);
  return stats.mmaped - stats.munmaped;
}

uptr __sanitizer_get_free_bytes() {
  AsanStats stats;
  GetAccumulatedStats(&stats);
  uptr total_free = stats.mmaped
                  - stats.munmaped
                  + stats.really_freed;
  uptr total_used = stats.malloced
                  + stats.malloced_redzones;
  // Return sane value if total_free < total_used due to racy
  // way we update accumulated stats.
  return (total_free > total_used) ? total_free - total_used : 1;
}

uptr __sanitizer_get_unmapped_bytes() {
  return 0;
}

void __asan_print_accumulated_stats() {
  PrintAccumulatedStats();
}
<commit_msg>[AArch64][asan] fix typo in AsanStats::Print<commit_after>//===-- asan_stats.cpp ----------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// This file is a part of AddressSanitizer, an address sanity checker.
//
// Code related to statistics collected by AddressSanitizer.
//===----------------------------------------------------------------------===//
#include "asan_allocator.h"
#include "asan_interceptors.h"
#include "asan_internal.h"
#include "asan_stats.h"
#include "asan_thread.h"
#include "sanitizer_common/sanitizer_allocator_interface.h"
#include "sanitizer_common/sanitizer_mutex.h"
#include "sanitizer_common/sanitizer_stackdepot.h"

namespace __asan {

AsanStats::AsanStats() {
  Clear();
}

void AsanStats::Clear() {
  CHECK(REAL(memset));
  REAL(memset)(this, 0, sizeof(AsanStats));
}

static void PrintMallocStatsArray(const char *prefix,
                                  uptr *array, uptr size) {
  Printf("%s", prefix);
  for (uptr i = 0; i < size; i++) {
    if (!array[i]) continue;
    Printf("%zu:%zu; ", i, array[i]);
  }
  Printf("\n");
}

void AsanStats::Print() {
  Printf("Stats: %zuM malloced (%zuM for red zones) by %zu calls\n",
             malloced>>20, malloced_redzones>>20, mallocs);
  Printf("Stats: %zuM realloced by %zu calls\n", realloced>>20, reallocs);
  Printf("Stats: %zuM freed by %zu calls\n", freed>>20, frees);
  Printf("Stats: %zuM really freed by %zu calls\n",
             really_freed>>20, real_frees);
  Printf("Stats: %zuM (%zuM-%zuM) mmaped; %zu maps, %zu unmaps\n",
             (mmaped-munmaped)>>20, mmaped>>20, munmaped>>20,
             mmaps, munmaps);

  PrintMallocStatsArray("  mallocs by size class: ", malloced_by_size,
                        get_allocator().KNumClasses());
  Printf("Stats: malloc large: %zu\n", malloc_large);
}

void AsanStats::MergeFrom(const AsanStats *stats) {
  uptr *dst_ptr = reinterpret_cast<uptr*>(this);
  const uptr *src_ptr = reinterpret_cast<const uptr*>(stats);
  uptr num_fields = sizeof(*this) / sizeof(uptr);
  for (uptr i = 0; i < num_fields; i++)
    dst_ptr[i] += src_ptr[i];
}

static BlockingMutex print_lock(LINKER_INITIALIZED);

static AsanStats unknown_thread_stats(LINKER_INITIALIZED);
static AsanStats dead_threads_stats(LINKER_INITIALIZED);
static BlockingMutex dead_threads_stats_lock(LINKER_INITIALIZED);
// Required for malloc_zone_statistics() on OS X. This can't be stored in
// per-thread AsanStats.
static uptr max_malloced_memory;

static void MergeThreadStats(ThreadContextBase *tctx_base, void *arg) {
  AsanStats *accumulated_stats = reinterpret_cast<AsanStats*>(arg);
  AsanThreadContext *tctx = static_cast<AsanThreadContext*>(tctx_base);
  if (AsanThread *t = tctx->thread)
    accumulated_stats->MergeFrom(&t->stats());
}

static void GetAccumulatedStats(AsanStats *stats) {
  stats->Clear();
  {
    ThreadRegistryLock l(&asanThreadRegistry());
    asanThreadRegistry()
        .RunCallbackForEachThreadLocked(MergeThreadStats, stats);
  }
  stats->MergeFrom(&unknown_thread_stats);
  {
    BlockingMutexLock lock(&dead_threads_stats_lock);
    stats->MergeFrom(&dead_threads_stats);
  }
  // This is not very accurate: we may miss allocation peaks that happen
  // between two updates of accumulated_stats_. For more accurate bookkeeping
  // the maximum should be updated on every malloc(), which is unacceptable.
  if (max_malloced_memory < stats->malloced) {
    max_malloced_memory = stats->malloced;
  }
}

void FlushToDeadThreadStats(AsanStats *stats) {
  BlockingMutexLock lock(&dead_threads_stats_lock);
  dead_threads_stats.MergeFrom(stats);
  stats->Clear();
}

void FillMallocStatistics(AsanMallocStats *malloc_stats) {
  AsanStats stats;
  GetAccumulatedStats(&stats);
  malloc_stats->blocks_in_use = stats.mallocs;
  malloc_stats->size_in_use = stats.malloced;
  malloc_stats->max_size_in_use = max_malloced_memory;
  malloc_stats->size_allocated = stats.mmaped;
}

AsanStats &GetCurrentThreadStats() {
  AsanThread *t = GetCurrentThread();
  return (t) ? t->stats() : unknown_thread_stats;
}

static void PrintAccumulatedStats() {
  AsanStats stats;
  GetAccumulatedStats(&stats);
  // Use lock to keep reports from mixing up.
  BlockingMutexLock lock(&print_lock);
  stats.Print();
  StackDepotStats *stack_depot_stats = StackDepotGetStats();
  Printf("Stats: StackDepot: %zd ids; %zdM allocated\n",
         stack_depot_stats->n_uniq_ids, stack_depot_stats->allocated >> 20);
  PrintInternalAllocatorStats();
}

}  // namespace __asan

// ---------------------- Interface ---------------- {{{1
using namespace __asan;  // NOLINT

uptr __sanitizer_get_current_allocated_bytes() {
  AsanStats stats;
  GetAccumulatedStats(&stats);
  uptr malloced = stats.malloced;
  uptr freed = stats.freed;
  // Return sane value if malloced < freed due to racy
  // way we update accumulated stats.
  return (malloced > freed) ? malloced - freed : 1;
}

uptr __sanitizer_get_heap_size() {
  AsanStats stats;
  GetAccumulatedStats(&stats);
  return stats.mmaped - stats.munmaped;
}

uptr __sanitizer_get_free_bytes() {
  AsanStats stats;
  GetAccumulatedStats(&stats);
  uptr total_free = stats.mmaped
                  - stats.munmaped
                  + stats.really_freed;
  uptr total_used = stats.malloced
                  + stats.malloced_redzones;
  // Return sane value if total_free < total_used due to racy
  // way we update accumulated stats.
  return (total_free > total_used) ? total_free - total_used : 1;
}

uptr __sanitizer_get_unmapped_bytes() {
  return 0;
}

void __asan_print_accumulated_stats() {
  PrintAccumulatedStats();
}
<|endoftext|>


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

#include "stopwatch.hxx"
#include "io/UniqueFileDescriptor.hxx"
#include "util/LeakDetector.hxx"
#include "util/StringBuilder.hxx"

#include <boost/container/static_vector.hpp>

#include <chrono>
#include <list>
#include <string>

#include <assert.h>
#include <stdio.h>
#include <string.h>

#if 0
#include <sys/resource.h>
#endif

static UniqueFileDescriptor stopwatch_fd;

struct StopwatchEvent {
	std::string name;

	std::chrono::steady_clock::time_point time =
		std::chrono::steady_clock::now();

	template<typename N>
	explicit StopwatchEvent(N &&_name) noexcept
		:name(std::forward<N>(_name)) {}
};

class Stopwatch final : LeakDetector
{
	const std::string name;

	const std::chrono::steady_clock::time_point time =
		std::chrono::steady_clock::now();

	std::list<std::shared_ptr<Stopwatch>> children;

	boost::container::static_vector<StopwatchEvent, 16> events;

#if 0
	/**
	 * Our own resource usage, measured when the stopwatch was
	 * started.
	 */
	struct rusage self;
#endif

	const bool dump;

public:
	template<typename N>
	Stopwatch(N &&_name, bool _dump) noexcept
		:name(std::forward<N>(_name)),
		 dump(_dump)
	{
#if 0
		getrusage(RUSAGE_SELF, &self);
#endif
	}

	~Stopwatch() noexcept {
		if (dump)
			Dump(time, 0);
	}

	template<typename C>
	void AddChild(C &&child) noexcept {
		children.emplace_back(std::forward<C>(child));
	}

	void RecordEvent(const char *name) noexcept;

	void Dump(std::chrono::steady_clock::time_point root_time,
		  size_t indent) noexcept;
};

void
stopwatch_enable(UniqueFileDescriptor fd) noexcept
{
	assert(fd.IsDefined());

	stopwatch_fd = std::move(fd);
}

bool
stopwatch_is_enabled() noexcept
{
	return stopwatch_fd.IsDefined();
}

static std::string
MakeStopwatchName(std::string name, const char *suffix) noexcept
{
	if (suffix != nullptr)
		name += suffix;

	constexpr size_t MAX_NAME = 96;
	if (name.length() > MAX_NAME)
		name.resize(MAX_NAME);

	return name;
}

static std::shared_ptr<Stopwatch>
stopwatch_new(const char *name, const char *suffix) noexcept
{
	if (!stopwatch_is_enabled())
		return nullptr;

	return std::make_shared<Stopwatch>(MakeStopwatchName(name, suffix), true);
}

StopwatchPtr::StopwatchPtr(const char *name, const char *suffix) noexcept
	:stopwatch(stopwatch_new(name, suffix)) {}

StopwatchPtr::StopwatchPtr(Stopwatch *parent, const char *name,
			   const char *suffix) noexcept
{
	if (parent != nullptr) {
		stopwatch = std::make_shared<Stopwatch>(MakeStopwatchName(name, suffix),
							false);
		parent->AddChild(stopwatch);
	}
}

StopwatchPtr::~StopwatchPtr() noexcept = default;

inline void
Stopwatch::RecordEvent(const char *event_name) noexcept
{
	if (events.size() >= events.capacity())
		/* array is full, do not record any more events */
		return;

	events.emplace_back(event_name);
}

void
StopwatchPtr::RecordEvent(const char *name) const noexcept
{
	if (stopwatch != nullptr)
		stopwatch->RecordEvent(name);
}

static constexpr long
ToLongMs(std::chrono::steady_clock::duration d) noexcept
{
	return std::chrono::duration_cast<std::chrono::milliseconds>(d).count();
}

#if 0

static long
timeval_diff_ms(const struct timeval *a, const struct timeval *b) noexcept
{
	return (a->tv_sec - b->tv_sec) * 1000 +
		(a->tv_usec - b->tv_usec) / 1000;
}

#endif

inline void
Stopwatch::Dump(std::chrono::steady_clock::time_point root_time,
		size_t indent) noexcept
try {
	if (!stopwatch_fd.IsDefined())
		return;

	char message[1024];
	StringBuilder b(message);

	b.CheckAppend(indent);
	std::fill_n(b.GetTail(), indent, ' ');
	b.Extend(indent);

	b.Append(name.c_str());

	b.Format(" init=%ldms",
		 ToLongMs(time - root_time));

	for (const auto &i : events)
		b.Format(" %s=%ldms",
			 i.name.c_str(),
			 ToLongMs(i.time - time));

#if 0
	struct rusage new_self;
	getrusage(RUSAGE_SELF, &new_self);
	b.Format(" (beng-proxy=%ld+%ldms)",
		 timeval_diff_ms(&new_self.ru_utime, &self.ru_utime),
		 timeval_diff_ms(&new_self.ru_stime, &self.ru_stime));
#endif

	b.Append('\n');

	if (stopwatch_fd.Write(message, strlen(message)) < 0) {
		stopwatch_fd.Close();
		return;
	}

	indent += 2;

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

#include "stopwatch.hxx"
#include "io/UniqueFileDescriptor.hxx"
#include "util/LeakDetector.hxx"
#include "util/StaticVector.hxx"
#include "util/StringBuilder.hxx"

#include <chrono>
#include <list>
#include <string>

#include <assert.h>
#include <stdio.h>
#include <string.h>

#if 0
#include <sys/resource.h>
#endif

static UniqueFileDescriptor stopwatch_fd;

struct StopwatchEvent {
	std::string name;

	std::chrono::steady_clock::time_point time =
		std::chrono::steady_clock::now();

	template<typename N>
	explicit StopwatchEvent(N &&_name) noexcept
		:name(std::forward<N>(_name)) {}
};

class Stopwatch final : LeakDetector
{
	const std::string name;

	const std::chrono::steady_clock::time_point time =
		std::chrono::steady_clock::now();

	std::list<std::shared_ptr<Stopwatch>> children;

	StaticVector<StopwatchEvent, 16> events;

#if 0
	/**
	 * Our own resource usage, measured when the stopwatch was
	 * started.
	 */
	struct rusage self;
#endif

	const bool dump;

public:
	template<typename N>
	Stopwatch(N &&_name, bool _dump) noexcept
		:name(std::forward<N>(_name)),
		 dump(_dump)
	{
#if 0
		getrusage(RUSAGE_SELF, &self);
#endif
	}

	~Stopwatch() noexcept {
		if (dump)
			Dump(time, 0);
	}

	template<typename C>
	void AddChild(C &&child) noexcept {
		children.emplace_back(std::forward<C>(child));
	}

	void RecordEvent(const char *name) noexcept;

	void Dump(std::chrono::steady_clock::time_point root_time,
		  size_t indent) noexcept;
};

void
stopwatch_enable(UniqueFileDescriptor fd) noexcept
{
	assert(fd.IsDefined());

	stopwatch_fd = std::move(fd);
}

bool
stopwatch_is_enabled() noexcept
{
	return stopwatch_fd.IsDefined();
}

static std::string
MakeStopwatchName(std::string name, const char *suffix) noexcept
{
	if (suffix != nullptr)
		name += suffix;

	constexpr size_t MAX_NAME = 96;
	if (name.length() > MAX_NAME)
		name.resize(MAX_NAME);

	return name;
}

static std::shared_ptr<Stopwatch>
stopwatch_new(const char *name, const char *suffix) noexcept
{
	if (!stopwatch_is_enabled())
		return nullptr;

	return std::make_shared<Stopwatch>(MakeStopwatchName(name, suffix), true);
}

StopwatchPtr::StopwatchPtr(const char *name, const char *suffix) noexcept
	:stopwatch(stopwatch_new(name, suffix)) {}

StopwatchPtr::StopwatchPtr(Stopwatch *parent, const char *name,
			   const char *suffix) noexcept
{
	if (parent != nullptr) {
		stopwatch = std::make_shared<Stopwatch>(MakeStopwatchName(name, suffix),
							false);
		parent->AddChild(stopwatch);
	}
}

StopwatchPtr::~StopwatchPtr() noexcept = default;

inline void
Stopwatch::RecordEvent(const char *event_name) noexcept
{
	if (events.full())
		/* array is full, do not record any more events */
		return;

	events.emplace_back(event_name);
}

void
StopwatchPtr::RecordEvent(const char *name) const noexcept
{
	if (stopwatch != nullptr)
		stopwatch->RecordEvent(name);
}

static constexpr long
ToLongMs(std::chrono::steady_clock::duration d) noexcept
{
	return std::chrono::duration_cast<std::chrono::milliseconds>(d).count();
}

#if 0

static long
timeval_diff_ms(const struct timeval *a, const struct timeval *b) noexcept
{
	return (a->tv_sec - b->tv_sec) * 1000 +
		(a->tv_usec - b->tv_usec) / 1000;
}

#endif

inline void
Stopwatch::Dump(std::chrono::steady_clock::time_point root_time,
		size_t indent) noexcept
try {
	if (!stopwatch_fd.IsDefined())
		return;

	char message[1024];
	StringBuilder b(message);

	b.CheckAppend(indent);
	std::fill_n(b.GetTail(), indent, ' ');
	b.Extend(indent);

	b.Append(name.c_str());

	b.Format(" init=%ldms",
		 ToLongMs(time - root_time));

	for (const auto &i : events)
		b.Format(" %s=%ldms",
			 i.name.c_str(),
			 ToLongMs(i.time - time));

#if 0
	struct rusage new_self;
	getrusage(RUSAGE_SELF, &new_self);
	b.Format(" (beng-proxy=%ld+%ldms)",
		 timeval_diff_ms(&new_self.ru_utime, &self.ru_utime),
		 timeval_diff_ms(&new_self.ru_stime, &self.ru_stime));
#endif

	b.Append('\n');

	if (stopwatch_fd.Write(message, strlen(message)) < 0) {
		stopwatch_fd.Close();
		return;
	}

	indent += 2;

	for (const auto &child : children)
		child->Dump(root_time, indent);
} catch (StringBuilder::Overflow) {
}
<|endoftext|>


<commit_before><commit_msg>Use using declarations instead of using namespace std<commit_after><|endoftext|>


<commit_before>/* Copyright 2012-present Facebook, Inc.
 * Licensed under the Apache License, Version 2.0 */

#include "watchman.h"

void stat_path(
    struct write_locked_watchman_root* lock,
    struct watchman_pending_collection* coll,
    const w_string& full_path,
    struct timeval now,
    int flags,
    struct watchman_dir_ent* pre_stat) {
  struct watchman_stat st;
  int res, err;
  char path[WATCHMAN_NAME_MAX];
  bool recursive = flags & W_PENDING_RECURSIVE;
  bool via_notify = flags & W_PENDING_VIA_NOTIFY;
  w_root_t *root = lock->root;

  if (w_ht_get(root->ignore.ignore_dirs, w_ht_ptr_val(full_path))) {
    w_log(
        W_LOG_DBG,
        "%.*s matches ignore_dir rules\n",
        int(full_path.size()),
        full_path.data());
    return;
  }

  if (full_path.size() > sizeof(path) - 1) {
    w_log(
        W_LOG_FATAL,
        "path %.*s is too big\n",
        int(full_path.size()),
        full_path.data());
  }

  memcpy(path, full_path.data(), full_path.size());
  path[full_path.size()] = 0;

  auto dir_name = full_path.dirName();
  auto file_name = full_path.baseName();
  auto dir = w_root_resolve_dir(lock, dir_name, true);

  auto file = dir->getChildFile(file_name);

  auto dir_ent = dir->getChildDir(file_name);

  if (pre_stat && pre_stat->has_stat) {
    memcpy(&st, &pre_stat->stat, sizeof(st));
    res = 0;
    err = 0;
  } else {
    struct stat struct_stat;
    res = w_lstat(path, &struct_stat, root->case_sensitive);
    err = res == 0 ? 0 : errno;
    w_log(W_LOG_DBG, "w_lstat(%s) file=%p dir=%p res=%d %s\n",
        path, file, dir_ent, res, strerror(err));
    if (err == 0) {
      struct_stat_to_watchman_stat(&struct_stat, &st);
    } else {
      // To suppress warning on win32
      memset(&st, 0, sizeof(st));
    }
  }

  if (res && (err == ENOENT || err == ENOTDIR)) {
    /* it's not there, update our state */
    if (dir_ent) {
      lock->root->inner.view->markDirDeleted(
          dir_ent, now, lock->root->inner.ticks, true);
      w_log(
          W_LOG_DBG,
          "w_lstat(%s) -> %s so stopping watch on %.*s\n",
          path,
          strerror(err),
          int(dir_name.size()),
          dir_name.data());
      stop_watching_dir(lock, dir_ent);
    }
    if (file) {
      if (file->exists) {
        w_log(W_LOG_DBG, "w_lstat(%s) -> %s so marking %.*s deleted\n", path,
              strerror(err), w_file_get_name(file)->len,
              w_file_get_name(file)->buf);
        file->exists = false;
        lock->root->inner.view->markFileChanged(
            file, now, lock->root->inner.ticks);
      }
    } else {
      // It was created and removed before we could ever observe it
      // in the filesystem.  We need to generate a deleted file
      // representation of it now, so that subscription clients can
      // be notified of this event
      file = lock->root->inner.view->getOrCreateChildFile(
          dir, file_name, now, lock->root->inner.ticks);
      w_log(W_LOG_DBG, "w_lstat(%s) -> %s and file node was NULL. "
          "Generating a deleted node.\n", path, strerror(err));
      file->exists = false;
      lock->root->inner.view->markFileChanged(
          file, now, lock->root->inner.ticks);
    }

    if (!root->case_sensitive && !w_string_equal(dir_name, root->root_path) &&
        dir->last_check_existed) {
      /* If we rejected the name because it wasn't canonical,
       * we need to ensure that we look in the parent dir to discover
       * the new item(s) */
      w_log(
          W_LOG_DBG,
          "we're case insensitive, and %s is ENOENT, "
          "speculatively look at parent dir %.*s\n",
          path,
          int(dir_name.size()),
          dir_name.data());
      w_pending_coll_add(coll, dir_name, now, W_PENDING_CRAWL_ONLY);
    }

  } else if (res) {
    w_log(W_LOG_ERR, "w_lstat(%s) %d %s\n",
        path, err, strerror(err));
  } else {
    if (!file) {
      file = lock->root->inner.view->getOrCreateChildFile(
          dir, file_name, now, lock->root->inner.ticks);
    }

    if (!file->exists) {
      /* we're transitioning from deleted to existing,
       * so we're effectively new again */
      file->ctime.ticks = root->inner.ticks;
      file->ctime.timestamp = now.tv_sec;
      /* if a dir was deleted and now exists again, we want
       * to crawl it again */
      recursive = true;
    }
    if (!file->exists || via_notify || did_file_change(&file->stat, &st)) {
      w_log(W_LOG_DBG,
          "file changed exists=%d via_notify=%d stat-changed=%d isdir=%d %s\n",
          (int)file->exists,
          (int)via_notify,
          (int)(file->exists && !via_notify),
          S_ISDIR(st.mode),
          path
      );
      file->exists = true;
      lock->root->inner.view->markFileChanged(
          file, now, lock->root->inner.ticks);
    }

    memcpy(&file->stat, &st, sizeof(file->stat));

#ifndef _WIN32
    // check for symbolic link
    if (S_ISLNK(st.mode)) {
      char link_target_path[WATCHMAN_NAME_MAX];
      ssize_t tlen = 0;

      tlen = readlink(path, link_target_path, sizeof(link_target_path));
      if (tlen < 0 || tlen >= WATCHMAN_NAME_MAX) {
        w_log(W_LOG_ERR,
            "readlink(%s) errno=%d tlen=%d\n", path, errno, (int)tlen);
        file->symlink_target.reset();
      } else {
        bool symlink_changed = false;
        w_string new_symlink_target(link_target_path, tlen, W_STRING_BYTE);
        if (file->symlink_target != new_symlink_target) {
          symlink_changed = true;
        }
        file->symlink_target = new_symlink_target;

        if (symlink_changed && cfg_get_bool(root, "watch_symlinks", false)) {
          w_pending_coll_add(
              &root->inner.pending_symlink_targets, full_path, now, 0);
        }
      }
    } else {
      file->symlink_target.reset();
    }
#endif

    if (S_ISDIR(st.mode)) {
      if (dir_ent == NULL) {
        recursive = true;
      } else {
        // Ensure that we believe that this node exists
        dir_ent->last_check_existed = true;
      }

      // Don't recurse if our parent is an ignore dir
      if (!w_ht_get(root->ignore.ignore_vcs, w_ht_ptr_val(dir_name)) ||
          // but do if we're looking at the cookie dir (stat_path is never
          // called for the root itself)
          w_string_equal(full_path, root->query_cookie_dir)) {
        if (!root->inner.watcher->flags & WATCHER_HAS_PER_FILE_NOTIFICATIONS) {
          /* we always need to crawl, but may not need to be fully recursive */
          w_pending_coll_add(coll, full_path, now,
              W_PENDING_CRAWL_ONLY | (recursive ? W_PENDING_RECURSIVE : 0));
        } else {
          /* we get told about changes on the child, so we only
           * need to crawl if we've never seen the dir before.
           * An exception is that fsevents will only report the root
           * of a dir rename and not a rename event for all of its
           * children. */
          if (recursive) {
            w_pending_coll_add(coll, full_path, now,
                W_PENDING_RECURSIVE|W_PENDING_CRAWL_ONLY);
          }
        }
      }
    } else if (dir_ent) {
      // We transitioned from dir to file (see fishy.php), so we should prune
      // our former tree here
      lock->root->inner.view->markDirDeleted(
          dir_ent, now, lock->root->inner.ticks, true);
    }
    if ((root->inner.watcher->flags & WATCHER_HAS_PER_FILE_NOTIFICATIONS) &&
        !S_ISDIR(st.mode) && !w_string_equal(dir_name, root->root_path) &&
        dir->last_check_existed) {
      /* Make sure we update the mtime on the parent directory.
       * We're deliberately not propagating any of the flags through; we
       * definitely don't want this to be a recursive evaluation and we
       * won'd want to treat this as VIA_NOTIFY to avoid spuriously
       * marking the node as changed when only its atime was changed.
       * https://github.com/facebook/watchman/issues/305 and
       * https://github.com/facebook/watchman/issues/307 have more
       * context on why this is.
       */
      w_pending_coll_add(coll, dir_name, now, 0);
    }
  }
}

/* vim:ts=2:sw=2:et:
 */
<commit_msg>cast view to InMemoryView explicitly in stat_path<commit_after>/* Copyright 2012-present Facebook, Inc.
 * Licensed under the Apache License, Version 2.0 */

#include "watchman.h"

void stat_path(
    struct write_locked_watchman_root* lock,
    struct watchman_pending_collection* coll,
    const w_string& full_path,
    struct timeval now,
    int flags,
    struct watchman_dir_ent* pre_stat) {
  struct watchman_stat st;
  int res, err;
  char path[WATCHMAN_NAME_MAX];
  bool recursive = flags & W_PENDING_RECURSIVE;
  bool via_notify = flags & W_PENDING_VIA_NOTIFY;
  w_root_t *root = lock->root;

  // stat_path is only invoked for instances of InMemoryView so we
  // need not check the return value of this cast expression.
  auto view =
      dynamic_cast<watchman::InMemoryView*>(lock->root->inner.view.get());

  if (w_ht_get(root->ignore.ignore_dirs, w_ht_ptr_val(full_path))) {
    w_log(
        W_LOG_DBG,
        "%.*s matches ignore_dir rules\n",
        int(full_path.size()),
        full_path.data());
    return;
  }

  if (full_path.size() > sizeof(path) - 1) {
    w_log(
        W_LOG_FATAL,
        "path %.*s is too big\n",
        int(full_path.size()),
        full_path.data());
  }

  memcpy(path, full_path.data(), full_path.size());
  path[full_path.size()] = 0;

  auto dir_name = full_path.dirName();
  auto file_name = full_path.baseName();
  auto dir = w_root_resolve_dir(lock, dir_name, true);

  auto file = dir->getChildFile(file_name);

  auto dir_ent = dir->getChildDir(file_name);

  if (pre_stat && pre_stat->has_stat) {
    memcpy(&st, &pre_stat->stat, sizeof(st));
    res = 0;
    err = 0;
  } else {
    struct stat struct_stat;
    res = w_lstat(path, &struct_stat, root->case_sensitive);
    err = res == 0 ? 0 : errno;
    w_log(W_LOG_DBG, "w_lstat(%s) file=%p dir=%p res=%d %s\n",
        path, file, dir_ent, res, strerror(err));
    if (err == 0) {
      struct_stat_to_watchman_stat(&struct_stat, &st);
    } else {
      // To suppress warning on win32
      memset(&st, 0, sizeof(st));
    }
  }

  if (res && (err == ENOENT || err == ENOTDIR)) {
    /* it's not there, update our state */
    if (dir_ent) {
      view->markDirDeleted(dir_ent, now, lock->root->inner.ticks, true);
      w_log(
          W_LOG_DBG,
          "w_lstat(%s) -> %s so stopping watch on %.*s\n",
          path,
          strerror(err),
          int(dir_name.size()),
          dir_name.data());
      stop_watching_dir(lock, dir_ent);
    }
    if (file) {
      if (file->exists) {
        w_log(W_LOG_DBG, "w_lstat(%s) -> %s so marking %.*s deleted\n", path,
              strerror(err), w_file_get_name(file)->len,
              w_file_get_name(file)->buf);
        file->exists = false;
        view->markFileChanged(file, now, lock->root->inner.ticks);
      }
    } else {
      // It was created and removed before we could ever observe it
      // in the filesystem.  We need to generate a deleted file
      // representation of it now, so that subscription clients can
      // be notified of this event
      file = view->getOrCreateChildFile(
          dir, file_name, now, lock->root->inner.ticks);
      w_log(W_LOG_DBG, "w_lstat(%s) -> %s and file node was NULL. "
          "Generating a deleted node.\n", path, strerror(err));
      file->exists = false;
      view->markFileChanged(file, now, lock->root->inner.ticks);
    }

    if (!root->case_sensitive && !w_string_equal(dir_name, root->root_path) &&
        dir->last_check_existed) {
      /* If we rejected the name because it wasn't canonical,
       * we need to ensure that we look in the parent dir to discover
       * the new item(s) */
      w_log(
          W_LOG_DBG,
          "we're case insensitive, and %s is ENOENT, "
          "speculatively look at parent dir %.*s\n",
          path,
          int(dir_name.size()),
          dir_name.data());
      w_pending_coll_add(coll, dir_name, now, W_PENDING_CRAWL_ONLY);
    }

  } else if (res) {
    w_log(W_LOG_ERR, "w_lstat(%s) %d %s\n",
        path, err, strerror(err));
  } else {
    if (!file) {
      file = view->getOrCreateChildFile(
          dir, file_name, now, lock->root->inner.ticks);
    }

    if (!file->exists) {
      /* we're transitioning from deleted to existing,
       * so we're effectively new again */
      file->ctime.ticks = root->inner.ticks;
      file->ctime.timestamp = now.tv_sec;
      /* if a dir was deleted and now exists again, we want
       * to crawl it again */
      recursive = true;
    }
    if (!file->exists || via_notify || did_file_change(&file->stat, &st)) {
      w_log(W_LOG_DBG,
          "file changed exists=%d via_notify=%d stat-changed=%d isdir=%d %s\n",
          (int)file->exists,
          (int)via_notify,
          (int)(file->exists && !via_notify),
          S_ISDIR(st.mode),
          path
      );
      file->exists = true;
      view->markFileChanged(file, now, lock->root->inner.ticks);
    }

    memcpy(&file->stat, &st, sizeof(file->stat));

#ifndef _WIN32
    // check for symbolic link
    if (S_ISLNK(st.mode)) {
      char link_target_path[WATCHMAN_NAME_MAX];
      ssize_t tlen = 0;

      tlen = readlink(path, link_target_path, sizeof(link_target_path));
      if (tlen < 0 || tlen >= WATCHMAN_NAME_MAX) {
        w_log(W_LOG_ERR,
            "readlink(%s) errno=%d tlen=%d\n", path, errno, (int)tlen);
        file->symlink_target.reset();
      } else {
        bool symlink_changed = false;
        w_string new_symlink_target(link_target_path, tlen, W_STRING_BYTE);
        if (file->symlink_target != new_symlink_target) {
          symlink_changed = true;
        }
        file->symlink_target = new_symlink_target;

        if (symlink_changed && cfg_get_bool(root, "watch_symlinks", false)) {
          w_pending_coll_add(
              &root->inner.pending_symlink_targets, full_path, now, 0);
        }
      }
    } else {
      file->symlink_target.reset();
    }
#endif

    if (S_ISDIR(st.mode)) {
      if (dir_ent == NULL) {
        recursive = true;
      } else {
        // Ensure that we believe that this node exists
        dir_ent->last_check_existed = true;
      }

      // Don't recurse if our parent is an ignore dir
      if (!w_ht_get(root->ignore.ignore_vcs, w_ht_ptr_val(dir_name)) ||
          // but do if we're looking at the cookie dir (stat_path is never
          // called for the root itself)
          w_string_equal(full_path, root->query_cookie_dir)) {
        if (!root->inner.watcher->flags & WATCHER_HAS_PER_FILE_NOTIFICATIONS) {
          /* we always need to crawl, but may not need to be fully recursive */
          w_pending_coll_add(coll, full_path, now,
              W_PENDING_CRAWL_ONLY | (recursive ? W_PENDING_RECURSIVE : 0));
        } else {
          /* we get told about changes on the child, so we only
           * need to crawl if we've never seen the dir before.
           * An exception is that fsevents will only report the root
           * of a dir rename and not a rename event for all of its
           * children. */
          if (recursive) {
            w_pending_coll_add(coll, full_path, now,
                W_PENDING_RECURSIVE|W_PENDING_CRAWL_ONLY);
          }
        }
      }
    } else if (dir_ent) {
      // We transitioned from dir to file (see fishy.php), so we should prune
      // our former tree here
      view->markDirDeleted(dir_ent, now, lock->root->inner.ticks, true);
    }
    if ((root->inner.watcher->flags & WATCHER_HAS_PER_FILE_NOTIFICATIONS) &&
        !S_ISDIR(st.mode) && !w_string_equal(dir_name, root->root_path) &&
        dir->last_check_existed) {
      /* Make sure we update the mtime on the parent directory.
       * We're deliberately not propagating any of the flags through; we
       * definitely don't want this to be a recursive evaluation and we
       * won'd want to treat this as VIA_NOTIFY to avoid spuriously
       * marking the node as changed when only its atime was changed.
       * https://github.com/facebook/watchman/issues/305 and
       * https://github.com/facebook/watchman/issues/307 have more
       * context on why this is.
       */
      w_pending_coll_add(coll, dir_name, now, 0);
    }
  }
}

/* vim:ts=2:sw=2:et:
 */
<|endoftext|>


<commit_before>/*
Copyright (c) 2003-2010 Sony Pictures Imageworks Inc., et al.
All Rights Reserved.

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

#include <OpenColorIO/OpenColorIO.h>

#include "Mutex.h"
#include "PathUtils.h"
#include "Platform.h"
#include "pystring/pystring.h"

#if !defined(WINDOWS)
#include <sys/param.h>
#else
#include <direct.h>
#define MAXPATHLEN 4096
#endif

#if defined(__APPLE__) && !defined(__IPHONE__)
#include <crt_externs.h> // _NSGetEnviron()
#include <unistd.h>
#elif !defined(WINDOWS)
#include <unistd.h>
extern char **environ;
#endif

OCIO_NAMESPACE_ENTER
{
    namespace
    {
        std::string ComputeHash(const std::string & filename)
        {
            struct stat results;
            if (stat(filename.c_str(), &results) == 0)
            {
                // Treat the mtime + inode as a proxy for the contents
                std::ostringstream fasthash;
                fasthash << results.st_ino << ":";
                fasthash << results.st_mtime;
                return fasthash.str();
            }

            return "";
        }

        // We mutex both the main map and each item individually, so that
        // the potentially slow stat calls dont block other lookups to already
        // existing items. (The stat calls will block other lookups on the
        // *same* file though).

        struct FileHashResult
        {
            Mutex mutex;
            std::string hash;
            bool ready;

            FileHashResult():
                ready(false)
            {}
        };

        typedef OCIO_SHARED_PTR<FileHashResult> FileHashResultPtr;
        typedef std::map<std::string, FileHashResultPtr> FileCacheMap;

        FileCacheMap g_fastFileHashCache;
        Mutex g_fastFileHashCache_mutex;
    }

    std::string GetFastFileHash(const std::string & filename)
    {
        FileHashResultPtr fileHashResultPtr;
        {
            AutoMutex lock(g_fastFileHashCache_mutex);
            FileCacheMap::iterator iter = g_fastFileHashCache.find(filename);
            if(iter != g_fastFileHashCache.end())
            {
                fileHashResultPtr = iter->second;
            }
            else
            {
                fileHashResultPtr = FileHashResultPtr(new FileHashResult);
                g_fastFileHashCache[filename] = fileHashResultPtr;
            }
        }

        std::string hash;
        {
            AutoMutex lock(fileHashResultPtr->mutex);
            if(!fileHashResultPtr->ready)
            {
                fileHashResultPtr->ready = true;
                fileHashResultPtr->hash = ComputeHash(filename);
            }

            hash = fileHashResultPtr->hash;
        }

        return hash;
    }

    bool FileExists(const std::string & filename)
    {
        std::string hash = GetFastFileHash(filename);
        return (!hash.empty());
    }

    void ClearPathCaches()
    {
        AutoMutex lock(g_fastFileHashCache_mutex);
        g_fastFileHashCache.clear();
    }

    namespace pystring
    {
    namespace os
    {
        std::string getcwd()
        {
#ifdef WINDOWS
            char path[MAXPATHLEN];
            _getcwd(path, MAXPATHLEN);
            return path;
#else
            char path[MAXPATHLEN];
            ::getcwd(path, MAXPATHLEN);
            return path;
#endif
        }

    namespace path
    {
        std::string abspath(const std::string & path)
        {
            std::string p = path;
            if(!isabs(p)) p = join(getcwd(), p);
            return normpath(p);
        }
    } // namespace path
    } // namespace os
    }

    namespace
    {
        inline char** GetEnviron()
        {
#if __IPHONE__
            // TODO: fix this
            return NULL;
#elif __APPLE__
            return (*_NSGetEnviron());
#else
            return environ;
#endif
        }

        const int MAX_PATH_LENGTH = 4096;
    }

    void LoadEnvironment(EnvMap & map, bool update)
    {
        for (char **env = GetEnviron(); *env != NULL; ++env)
        {

            // split environment up into std::map[name] = value
            std::string env_str = (char*)*env;
            int pos = static_cast<int>(env_str.find_first_of('='));
            std::string name = env_str.substr(0, pos);
            std::string value = env_str.substr(pos+1, env_str.length());

            if(update)
            {
                // update existing key:values that match
                EnvMap::iterator iter = map.find(name);
                if(iter != map.end()) iter->second = value;
            }
            else
            {
                map.insert(EnvMap::value_type(name, value));
            }
        }
    }

    std::string EnvExpand(const std::string & str, const EnvMap & map)
    {
        // Early exit if no magic characters are found.
        if(pystring::find(str, "$") == -1 &&
           pystring::find(str, "%") == -1) return str;

        std::string orig = str;
        std::string newstr = str;

        // This walks through the envmap in key order,
        // from longest to shortest to handle envvars which are
        // substrings.
        // ie. '$TEST_$TESTING_$TE' will expand in this order '2 1 3'

        for (EnvMap::const_iterator iter = map.begin();
             iter != map.end(); ++iter)
        {
            newstr = pystring::replace(newstr,
                ("${"+iter->first+"}"), iter->second);
            newstr = pystring::replace(newstr,
                ("$"+iter->first), iter->second);
            newstr = pystring::replace(newstr,
                ("%"+iter->first+"%"), iter->second);
        }

        // recursively call till string doesn't expand anymore
        if(newstr != orig)
        {
            return EnvExpand(newstr, map);
        }

        return orig;
    }
}
OCIO_NAMESPACE_EXIT

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

#ifdef OCIO_UNIT_TEST

namespace OCIO = OCIO_NAMESPACE;
#include "UnitTest.h"

OIIO_ADD_TEST(PathUtils, EnvExpand)
{
    // build env by hand for unit test
    OCIO::EnvMap env_map; // = OCIO::GetEnvMap();

    // add some fake env vars so the test runs
    env_map.insert(OCIO::EnvMap::value_type("TEST1", "foo.bar"));
    env_map.insert(OCIO::EnvMap::value_type("TEST1NG", "bar.foo"));
    env_map.insert(OCIO::EnvMap::value_type("FOO_foo.bar", "cheese"));

    //
    std::string foo = "/a/b/${TEST1}/${TEST1NG}/$TEST1/$TEST1NG/${FOO_${TEST1}}/";
    std::string foo_result = "/a/b/foo.bar/bar.foo/foo.bar/bar.foo/cheese/";
    std::string testresult = OCIO::EnvExpand(foo, env_map);
    OIIO_CHECK_ASSERT( testresult == foo_result );
}

#endif // OCIO_BUILD_TESTS
<commit_msg>Fix getcwd to not rely on MAXPATHLEN<commit_after>/*
Copyright (c) 2003-2010 Sony Pictures Imageworks Inc., et al.
All Rights Reserved.

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

#include <OpenColorIO/OpenColorIO.h>

#include "Mutex.h"
#include "PathUtils.h"
#include "Platform.h"
#include "pystring/pystring.h"

#if !defined(WINDOWS)
#include <sys/param.h>
#else
#include <direct.h>
#define MAXPATHLEN 4096
#endif

#if defined(__APPLE__) && !defined(__IPHONE__)
#include <crt_externs.h> // _NSGetEnviron()
#include <unistd.h>
#elif !defined(WINDOWS)
#include <unistd.h>
extern char **environ;
#endif

OCIO_NAMESPACE_ENTER
{
    namespace
    {
        std::string ComputeHash(const std::string & filename)
        {
            struct stat results;
            if (stat(filename.c_str(), &results) == 0)
            {
                // Treat the mtime + inode as a proxy for the contents
                std::ostringstream fasthash;
                fasthash << results.st_ino << ":";
                fasthash << results.st_mtime;
                return fasthash.str();
            }

            return "";
        }

        // We mutex both the main map and each item individually, so that
        // the potentially slow stat calls dont block other lookups to already
        // existing items. (The stat calls will block other lookups on the
        // *same* file though).

        struct FileHashResult
        {
            Mutex mutex;
            std::string hash;
            bool ready;

            FileHashResult():
                ready(false)
            {}
        };

        typedef OCIO_SHARED_PTR<FileHashResult> FileHashResultPtr;
        typedef std::map<std::string, FileHashResultPtr> FileCacheMap;

        FileCacheMap g_fastFileHashCache;
        Mutex g_fastFileHashCache_mutex;
    }

    std::string GetFastFileHash(const std::string & filename)
    {
        FileHashResultPtr fileHashResultPtr;
        {
            AutoMutex lock(g_fastFileHashCache_mutex);
            FileCacheMap::iterator iter = g_fastFileHashCache.find(filename);
            if(iter != g_fastFileHashCache.end())
            {
                fileHashResultPtr = iter->second;
            }
            else
            {
                fileHashResultPtr = FileHashResultPtr(new FileHashResult);
                g_fastFileHashCache[filename] = fileHashResultPtr;
            }
        }

        std::string hash;
        {
            AutoMutex lock(fileHashResultPtr->mutex);
            if(!fileHashResultPtr->ready)
            {
                fileHashResultPtr->ready = true;
                fileHashResultPtr->hash = ComputeHash(filename);
            }

            hash = fileHashResultPtr->hash;
        }

        return hash;
    }

    bool FileExists(const std::string & filename)
    {
        std::string hash = GetFastFileHash(filename);
        return (!hash.empty());
    }

    void ClearPathCaches()
    {
        AutoMutex lock(g_fastFileHashCache_mutex);
        g_fastFileHashCache.clear();
    }

    namespace pystring
    {
    namespace os
    {
        std::string getcwd()
        {
#ifdef WINDOWS
            char path[MAXPATHLEN];
            _getcwd(path, MAXPATHLEN);
            return path;
#else
            std::vector<char> current_dir;
#ifdef PATH_MAX
            current_dir.resize(PATH_MAX);
#else
            current_dir.resize(1024);
#endif
            while (::getcwd(&current_dir[0], current_dir.size()) == NULL && errno == ERANGE) {
                current_dir.resize(current_dir.size() + 1024);
            }

            return std::string(&current_dir[0]);
#endif
        }

    namespace path
    {
        std::string abspath(const std::string & path)
        {
            std::string p = path;
            if(!isabs(p)) p = join(getcwd(), p);
            return normpath(p);
        }
    } // namespace path
    } // namespace os
    }

    namespace
    {
        inline char** GetEnviron()
        {
#if __IPHONE__
            // TODO: fix this
            return NULL;
#elif __APPLE__
            return (*_NSGetEnviron());
#else
            return environ;
#endif
        }

        const int MAX_PATH_LENGTH = 4096;
    }

    void LoadEnvironment(EnvMap & map, bool update)
    {
        for (char **env = GetEnviron(); *env != NULL; ++env)
        {

            // split environment up into std::map[name] = value
            std::string env_str = (char*)*env;
            int pos = static_cast<int>(env_str.find_first_of('='));
            std::string name = env_str.substr(0, pos);
            std::string value = env_str.substr(pos+1, env_str.length());

            if(update)
            {
                // update existing key:values that match
                EnvMap::iterator iter = map.find(name);
                if(iter != map.end()) iter->second = value;
            }
            else
            {
                map.insert(EnvMap::value_type(name, value));
            }
        }
    }

    std::string EnvExpand(const std::string & str, const EnvMap & map)
    {
        // Early exit if no magic characters are found.
        if(pystring::find(str, "$") == -1 &&
           pystring::find(str, "%") == -1) return str;

        std::string orig = str;
        std::string newstr = str;

        // This walks through the envmap in key order,
        // from longest to shortest to handle envvars which are
        // substrings.
        // ie. '$TEST_$TESTING_$TE' will expand in this order '2 1 3'

        for (EnvMap::const_iterator iter = map.begin();
             iter != map.end(); ++iter)
        {
            newstr = pystring::replace(newstr,
                ("${"+iter->first+"}"), iter->second);
            newstr = pystring::replace(newstr,
                ("$"+iter->first), iter->second);
            newstr = pystring::replace(newstr,
                ("%"+iter->first+"%"), iter->second);
        }

        // recursively call till string doesn't expand anymore
        if(newstr != orig)
        {
            return EnvExpand(newstr, map);
        }

        return orig;
    }
}
OCIO_NAMESPACE_EXIT

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

#ifdef OCIO_UNIT_TEST

namespace OCIO = OCIO_NAMESPACE;
#include "UnitTest.h"

OIIO_ADD_TEST(PathUtils, EnvExpand)
{
    // build env by hand for unit test
    OCIO::EnvMap env_map; // = OCIO::GetEnvMap();

    // add some fake env vars so the test runs
    env_map.insert(OCIO::EnvMap::value_type("TEST1", "foo.bar"));
    env_map.insert(OCIO::EnvMap::value_type("TEST1NG", "bar.foo"));
    env_map.insert(OCIO::EnvMap::value_type("FOO_foo.bar", "cheese"));

    //
    std::string foo = "/a/b/${TEST1}/${TEST1NG}/$TEST1/$TEST1NG/${FOO_${TEST1}}/";
    std::string foo_result = "/a/b/foo.bar/bar.foo/foo.bar/bar.foo/cheese/";
    std::string testresult = OCIO::EnvExpand(foo, env_map);
    OIIO_CHECK_ASSERT( testresult == foo_result );
}

#endif // OCIO_BUILD_TESTS
<|endoftext|>


<commit_before>/*
 * Copyright 2018 Google Inc.
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

#include "include/private/SkMalloc.h"
#include "include/private/SkTDArray.h"
#include "include/private/SkTo.h"

#include <utility>

namespace {
size_t offset(int n, size_t sizeOfT) {
    return SkToSizeT(n) * sizeOfT;
}

size_t mem_size(int n, size_t sizeOfT) {
    return SkToSizeT(n) * sizeOfT;
}
}  // namespace

SkTDStorage::SkTDStorage(SkTDStorage&& that)
        : fStorage{std::move(std::exchange(that.fStorage, nullptr))}
        , fReserve{std::exchange(that.fReserve, 0)}
        , fCount{std::exchange(that.fCount, 0)} {}

SkTDStorage& SkTDStorage::operator=(SkTDStorage&& that) {
    if (this != &that) {
        this->~SkTDStorage();
        new (this) SkTDStorage{std::move(that)};
    }
    return *this;
}

SkTDStorage::~SkTDStorage() {
    sk_free(fStorage);
}

void SkTDStorage::reset() {
    this->~SkTDStorage();
    new (this) SkTDStorage{};
}

void SkTDStorage::assign(const void* src, int count, size_t sizeOfT) {
    SkASSERT(count >= 0);
    if (count > 0) {
        fCount = count;
        size_t byteSize = this->size_bytes(sizeOfT);
        if (fCount > fReserve) {
            fStorage = static_cast<std::byte*>(sk_realloc_throw(fStorage, byteSize));
            fReserve = fCount;
        }
        memcpy(fStorage, src, byteSize);
    }
}

void SkTDStorage::resize(int newCount, size_t sizeOfT) {
    SkASSERT(newCount >= 0);
    if (fReserve < newCount) {
        this->reserve(newCount, sizeOfT);
    }
    fCount = newCount;
}

size_t SkTDStorage::size_bytes(size_t sizeOfT) const {
    return offset(fCount, sizeOfT);
}

void SkTDStorage::reserve(size_t reserveSize, size_t sizeOfT) {
    // Note: this takes a size_t to centralize size checking.
    SkASSERT_RELEASE(SkTFitsIn<int>(reserveSize));

    if (SkToInt(reserveSize) > fReserve) {
        // Establish the maximum number of elements that includes a valid count for end. In the
        // largest case end() = &fArray[INT_MAX] which is 1 after the last indexable element.
        static constexpr int kMaxCount = INT_MAX;

        // Assume that the array will max out.
        int expandedReserve = kMaxCount;
        int newReserve = SkToInt(reserveSize);
        if (kMaxCount - newReserve > 4) {
            // Add 1/4 more than we need. Add 4 to ensure this grows by at least 1. Pin to
            // kMaxCount if no room for 1/4 growth.
            int growth = 4 + ((newReserve + 4) >> 2);
            // Read this line as: if (count + growth < kMaxCount) { ... }
            // It's rewritten to avoid signed integer overflow.
            if (kMaxCount - newReserve > growth) {
                expandedReserve = newReserve + growth;
            }
        }

        fReserve = expandedReserve;
        fStorage = static_cast<std::byte*>(sk_realloc_throw(fStorage, mem_size(fReserve, sizeOfT)));
    }
}

void SkTDStorage::shrinkToFit(size_t sizeOfT) {
    // Because calling realloc with size of 0 is implementation defined, force to a good state by
    // freeing fStorage and setting reserve to 0.
    if (fCount != 0) {
        fReserve = fCount;
        fStorage = static_cast<std::byte*>(sk_realloc_throw(fStorage, mem_size(fReserve, sizeOfT)));
    } else {
        fReserve = 0;
        sk_free(fStorage);
        fStorage = nullptr;
    }
}

void* SkTDStorage::erase(int index, int count, size_t sizeOfT) {
    SkASSERT(index >= 0);
    SkASSERT(count >= 0);
    SkASSERT(index + count <= fCount);

    // Check that the resulting size fits in an int. This will abort if not.
    const int newCount = this->calculateSizeDeltaOrDie(-count);

    const int tailBegin = index + count;
    const size_t tailBeginOffset = offset(tailBegin, sizeOfT);
    if (count > 0) {
        // Move the tail down if there is one.
        if (tailBegin < fCount) {
            const size_t headEndOffset = offset(index, sizeOfT);
            const size_t gapSize = this->size_bytes(sizeOfT) - tailBeginOffset;
            memmove(fStorage + headEndOffset, fStorage + tailBeginOffset, gapSize);
        }
        this->resize(newCount, sizeOfT);
    }

    return fStorage + tailBeginOffset;
}

void* SkTDStorage::removeShuffle(int index, size_t sizeOfT) {
    SkASSERT(0 <= index && index < fCount);
    // Check that the new count is valid.
    int newCount = this->calculateSizeDeltaOrDie(-1);

    const size_t indexOffset = offset(index, sizeOfT);
    const size_t lastElementOffset = offset(newCount, sizeOfT);

    // Fill the index if not the last element.
    if (index < newCount) {
        memmove(fStorage + indexOffset, fStorage + lastElementOffset, sizeOfT);
    }
    this->resize(newCount, sizeOfT);
    return fStorage + lastElementOffset;
}

void* SkTDStorage::prepend(size_t sizeOfT) {
    return this->insert(/*index=*/0, sizeOfT);
}

void* SkTDStorage::append(size_t sizeOfT) {
    return this->insert(fCount, sizeOfT);
}

void* SkTDStorage::append(const void* src, int count, size_t sizeOfT) {
    return this->insert(fCount, src, count, sizeOfT);
}

void* SkTDStorage::insert(int index, size_t sizeOfT) {
    return this->insert(index, nullptr, /*count=*/1, sizeOfT);
}

void* SkTDStorage::insert(int index, const void* src, int count, size_t sizeOfT) {
    SkASSERT(0 <= index && index <= fCount);
    SkASSERT(count >= 0);
    const size_t indexOffset = offset(index, sizeOfT);

    if (count > 0) {
        const int oldCount = fCount;

        size_t oldCountOffset = this->size_bytes(sizeOfT);

        const int newCount = this->calculateSizeDeltaOrDie(count);
        this->resize(newCount, sizeOfT);

        // Shift memory to make space.
        if (index < oldCount) {
            // Safe because index <= oldCount and oldCount + count is safe.
            size_t shiftOffset = offset(index + count, sizeOfT);

            // Move the tail of data from index to oldCount.
            memmove(fStorage + shiftOffset, fStorage + indexOffset, oldCountOffset - indexOffset);
        }

        if (src != nullptr) {
            memcpy(fStorage + indexOffset, src, mem_size(count, sizeOfT));
        }
    }

    return fStorage + indexOffset;
}

int SkTDStorage::calculateSizeDeltaOrDie(int delta) const {
    // Check that count will not go negative.
    SkASSERT_RELEASE(-fCount <= delta);

    // We take care to avoid overflow here.
    // Because fCount and delta are both signed 32-bit ints, the sum of fCount and delta is at
    // most 4294967294, which fits fine in uint32_t. Proof follows in assert.
    static_assert(UINT32_MAX >= (uint32_t)INT_MAX + (uint32_t)INT_MAX);
    uint32_t count = (uint32_t)this->size() + (uint32_t)delta;
    SkASSERT_RELEASE(SkTFitsIn<int>(count));
    return SkToInt(count);
}

<commit_msg>Fix SkTDArray::operator=<commit_after>/*
 * Copyright 2018 Google Inc.
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

#include "include/private/SkMalloc.h"
#include "include/private/SkTDArray.h"
#include "include/private/SkTo.h"

#include <utility>

namespace {
size_t offset(int n, size_t sizeOfT) {
    return SkToSizeT(n) * sizeOfT;
}

size_t mem_size(int n, size_t sizeOfT) {
    return SkToSizeT(n) * sizeOfT;
}
}  // namespace

SkTDStorage::SkTDStorage(SkTDStorage&& that)
        : fStorage{std::move(std::exchange(that.fStorage, nullptr))}
        , fReserve{std::exchange(that.fReserve, 0)}
        , fCount{std::exchange(that.fCount, 0)} {}

SkTDStorage& SkTDStorage::operator=(SkTDStorage&& that) {
    if (this != &that) {
        this->~SkTDStorage();
        new (this) SkTDStorage{std::move(that)};
    }
    return *this;
}

SkTDStorage::~SkTDStorage() {
    sk_free(fStorage);
}

void SkTDStorage::reset() {
    this->~SkTDStorage();
    new (this) SkTDStorage{};
}

void SkTDStorage::assign(const void* src, int count, size_t sizeOfT) {
    SkASSERT(count >= 0);
    fCount = count;
    this->shrinkToFit(sizeOfT);
    if (count > 0 && src != nullptr) {
        memcpy(fStorage, src, this->size_bytes(sizeOfT));
    }
}

void SkTDStorage::resize(int newCount, size_t sizeOfT) {
    SkASSERT(newCount >= 0);
    if (fReserve < newCount) {
        this->reserve(newCount, sizeOfT);
    }
    fCount = newCount;
}

size_t SkTDStorage::size_bytes(size_t sizeOfT) const {
    return offset(fCount, sizeOfT);
}

void SkTDStorage::reserve(size_t reserveSize, size_t sizeOfT) {
    // Note: this takes a size_t to centralize size checking.
    SkASSERT_RELEASE(SkTFitsIn<int>(reserveSize));

    if (SkToInt(reserveSize) > fReserve) {
        // Establish the maximum number of elements that includes a valid count for end. In the
        // largest case end() = &fArray[INT_MAX] which is 1 after the last indexable element.
        static constexpr int kMaxCount = INT_MAX;

        // Assume that the array will max out.
        int expandedReserve = kMaxCount;
        int newReserve = SkToInt(reserveSize);
        if (kMaxCount - newReserve > 4) {
            // Add 1/4 more than we need. Add 4 to ensure this grows by at least 1. Pin to
            // kMaxCount if no room for 1/4 growth.
            int growth = 4 + ((newReserve + 4) >> 2);
            // Read this line as: if (count + growth < kMaxCount) { ... }
            // It's rewritten to avoid signed integer overflow.
            if (kMaxCount - newReserve > growth) {
                expandedReserve = newReserve + growth;
            }
        }

        fReserve = expandedReserve;
        fStorage = static_cast<std::byte*>(sk_realloc_throw(fStorage, mem_size(fReserve, sizeOfT)));
    }
}

void SkTDStorage::shrinkToFit(size_t sizeOfT) {
    // Because calling realloc with size of 0 is implementation defined, force to a good state by
    // freeing fStorage and setting reserve to 0.
    if (fCount != 0) {
        fReserve = fCount;
        fStorage = static_cast<std::byte*>(sk_realloc_throw(fStorage, mem_size(fReserve, sizeOfT)));
    } else {
        fReserve = 0;
        sk_free(fStorage);
        fStorage = nullptr;
    }
}

void* SkTDStorage::erase(int index, int count, size_t sizeOfT) {
    SkASSERT(index >= 0);
    SkASSERT(count >= 0);
    SkASSERT(index + count <= fCount);

    // Check that the resulting size fits in an int. This will abort if not.
    const int newCount = this->calculateSizeDeltaOrDie(-count);

    const int tailBegin = index + count;
    const size_t tailBeginOffset = offset(tailBegin, sizeOfT);
    if (count > 0) {
        // Move the tail down if there is one.
        if (tailBegin < fCount) {
            const size_t headEndOffset = offset(index, sizeOfT);
            const size_t gapSize = this->size_bytes(sizeOfT) - tailBeginOffset;
            memmove(fStorage + headEndOffset, fStorage + tailBeginOffset, gapSize);
        }
        this->resize(newCount, sizeOfT);
    }

    return fStorage + tailBeginOffset;
}

void* SkTDStorage::removeShuffle(int index, size_t sizeOfT) {
    SkASSERT(0 <= index && index < fCount);
    // Check that the new count is valid.
    int newCount = this->calculateSizeDeltaOrDie(-1);

    const size_t indexOffset = offset(index, sizeOfT);
    const size_t lastElementOffset = offset(newCount, sizeOfT);

    // Fill the index if not the last element.
    if (index < newCount) {
        memmove(fStorage + indexOffset, fStorage + lastElementOffset, sizeOfT);
    }
    this->resize(newCount, sizeOfT);
    return fStorage + lastElementOffset;
}

void* SkTDStorage::prepend(size_t sizeOfT) {
    return this->insert(/*index=*/0, sizeOfT);
}

void* SkTDStorage::append(size_t sizeOfT) {
    return this->insert(fCount, sizeOfT);
}

void* SkTDStorage::append(const void* src, int count, size_t sizeOfT) {
    return this->insert(fCount, src, count, sizeOfT);
}

void* SkTDStorage::insert(int index, size_t sizeOfT) {
    return this->insert(index, nullptr, /*count=*/1, sizeOfT);
}

void* SkTDStorage::insert(int index, const void* src, int count, size_t sizeOfT) {
    SkASSERT(0 <= index && index <= fCount);
    SkASSERT(count >= 0);
    const size_t indexOffset = offset(index, sizeOfT);

    if (count > 0) {
        const int oldCount = fCount;

        size_t oldCountOffset = this->size_bytes(sizeOfT);

        const int newCount = this->calculateSizeDeltaOrDie(count);
        this->resize(newCount, sizeOfT);

        // Shift memory to make space.
        if (index < oldCount) {
            // Safe because index <= oldCount and oldCount + count is safe.
            size_t shiftOffset = offset(index + count, sizeOfT);

            // Move the tail of data from index to oldCount.
            memmove(fStorage + shiftOffset, fStorage + indexOffset, oldCountOffset - indexOffset);
        }

        if (src != nullptr) {
            memcpy(fStorage + indexOffset, src, mem_size(count, sizeOfT));
        }
    }

    return fStorage + indexOffset;
}

int SkTDStorage::calculateSizeDeltaOrDie(int delta) const {
    // Check that count will not go negative.
    SkASSERT_RELEASE(-fCount <= delta);

    // We take care to avoid overflow here.
    // Because fCount and delta are both signed 32-bit ints, the sum of fCount and delta is at
    // most 4294967294, which fits fine in uint32_t. Proof follows in assert.
    static_assert(UINT32_MAX >= (uint32_t)INT_MAX + (uint32_t)INT_MAX);
    uint32_t count = (uint32_t)this->size() + (uint32_t)delta;
    SkASSERT_RELEASE(SkTFitsIn<int>(count));
    return SkToInt(count);
}

<|endoftext|>