diff options
Diffstat (limited to 'Source/JavaScriptCore/wtf')
257 files changed, 56845 insertions, 0 deletions
diff --git a/Source/JavaScriptCore/wtf/ASCIICType.h b/Source/JavaScriptCore/wtf/ASCIICType.h new file mode 100644 index 000000000..17006ae2d --- /dev/null +++ b/Source/JavaScriptCore/wtf/ASCIICType.h @@ -0,0 +1,161 @@ +/* + * Copyright (C) 2007, 2008, 2009, 2011 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of Apple Computer, Inc. ("Apple") 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 APPLE AND ITS 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 APPLE OR ITS 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. + */ + +#ifndef WTF_ASCIICType_h +#define WTF_ASCIICType_h + +#include <wtf/Assertions.h> + +// The behavior of many of the functions in the <ctype.h> header is dependent +// on the current locale. But in the WebKit project, all uses of those functions +// are in code processing something that's not locale-specific. These equivalents +// for some of the <ctype.h> functions are named more explicitly, not dependent +// on the C library locale, and we should also optimize them as needed. + +// All functions return false or leave the character unchanged if passed a character +// that is outside the range 0-7F. So they can be used on Unicode strings or +// characters if the intent is to do processing only if the character is ASCII. + +namespace WTF { + +template<typename CharType> inline bool isASCII(CharType c) +{ + return !(c & ~0x7F); +} + +template<typename CharType> inline bool isASCIIAlpha(CharType c) +{ + return (c | 0x20) >= 'a' && (c | 0x20) <= 'z'; +} + +template<typename CharType> inline bool isASCIIDigit(CharType c) +{ + return c >= '0' && c <= '9'; +} + +template<typename CharType> inline bool isASCIIAlphanumeric(CharType c) +{ + return isASCIIDigit(c) || isASCIIAlpha(c); +} + +template<typename CharType> inline bool isASCIIHexDigit(CharType c) +{ + return isASCIIDigit(c) || ((c | 0x20) >= 'a' && (c | 0x20) <= 'f'); +} + +template<typename CharType> inline bool isASCIILower(CharType c) +{ + return c >= 'a' && c <= 'z'; +} + +template<typename CharType> inline bool isASCIIOctalDigit(CharType c) +{ + return (c >= '0') & (c <= '7'); +} + +template<typename CharType> inline bool isASCIIPrintable(CharType c) +{ + return c >= ' ' && c <= '~'; +} + +/* + Statistics from a run of Apple's page load test for callers of isASCIISpace: + + character count + --------- ----- + non-spaces 689383 + 20 space 294720 + 0A \n 89059 + 09 \t 28320 + 0D \r 0 + 0C \f 0 + 0B \v 0 + */ +template<typename CharType> inline bool isASCIISpace(CharType c) +{ + return c <= ' ' && (c == ' ' || (c <= 0xD && c >= 0x9)); +} + +template<typename CharType> inline bool isASCIIUpper(CharType c) +{ + return c >= 'A' && c <= 'Z'; +} + +template<typename CharType> inline CharType toASCIILower(CharType c) +{ + return c | ((c >= 'A' && c <= 'Z') << 5); +} + +template<typename CharType> inline CharType toASCIIUpper(CharType c) +{ + return c & ~((c >= 'a' && c <= 'z') << 5); +} + +template<typename CharType> inline int toASCIIHexValue(CharType c) +{ + ASSERT(isASCIIHexDigit(c)); + return c < 'A' ? c - '0' : (c - 'A' + 10) & 0xF; +} + +template<typename CharType> inline int toASCIIHexValue(CharType upperValue, CharType lowerValue) +{ + ASSERT(isASCIIHexDigit(upperValue) && isASCIIHexDigit(lowerValue)); + return ((toASCIIHexValue(upperValue) << 4) & 0xF0) | toASCIIHexValue(lowerValue); +} + +inline char lowerNibbleToASCIIHexDigit(char c) +{ + char nibble = c & 0xF; + return nibble < 10 ? '0' + nibble : 'A' + nibble - 10; +} + +inline char upperNibbleToASCIIHexDigit(char c) +{ + char nibble = (c >> 4) & 0xF; + return nibble < 10 ? '0' + nibble : 'A' + nibble - 10; +} + +} + +using WTF::isASCII; +using WTF::isASCIIAlpha; +using WTF::isASCIIAlphanumeric; +using WTF::isASCIIDigit; +using WTF::isASCIIHexDigit; +using WTF::isASCIILower; +using WTF::isASCIIOctalDigit; +using WTF::isASCIIPrintable; +using WTF::isASCIISpace; +using WTF::isASCIIUpper; +using WTF::toASCIIHexValue; +using WTF::toASCIILower; +using WTF::toASCIIUpper; +using WTF::lowerNibbleToASCIIHexDigit; +using WTF::upperNibbleToASCIIHexDigit; + +#endif diff --git a/Source/JavaScriptCore/wtf/AVLTree.h b/Source/JavaScriptCore/wtf/AVLTree.h new file mode 100644 index 000000000..ec8a63951 --- /dev/null +++ b/Source/JavaScriptCore/wtf/AVLTree.h @@ -0,0 +1,960 @@ +/* + * Copyright (C) 2008 Apple Inc. All rights reserved. + * + * Based on Abstract AVL Tree Template v1.5 by Walt Karas + * <http://geocities.com/wkaras/gen_cpp/avl_tree.html>. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of Apple Computer, Inc. ("Apple") 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 APPLE AND ITS 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 APPLE OR ITS 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. + */ + +#ifndef AVL_TREE_H_ +#define AVL_TREE_H_ + +#include "Assertions.h" +#include <wtf/FixedArray.h> + +namespace WTF { + +// Here is the reference class for BSet. +// +// class BSet +// { +// public: +// +// class ANY_bitref +// { +// public: +// operator bool (); +// void operator = (bool b); +// }; +// +// // Does not have to initialize bits. +// BSet(); +// +// // Must return a valid value for index when 0 <= index < maxDepth +// ANY_bitref operator [] (unsigned index); +// +// // Set all bits to 1. +// void set(); +// +// // Set all bits to 0. +// void reset(); +// }; + +template<unsigned maxDepth> +class AVLTreeDefaultBSet { +public: + bool& operator[](unsigned i) { ASSERT(i < maxDepth); return m_data[i]; } + void set() { for (unsigned i = 0; i < maxDepth; ++i) m_data[i] = true; } + void reset() { for (unsigned i = 0; i < maxDepth; ++i) m_data[i] = false; } + +private: + FixedArray<bool, maxDepth> m_data; +}; + +// How to determine maxDepth: +// d Minimum number of nodes +// 2 2 +// 3 4 +// 4 7 +// 5 12 +// 6 20 +// 7 33 +// 8 54 +// 9 88 +// 10 143 +// 11 232 +// 12 376 +// 13 609 +// 14 986 +// 15 1,596 +// 16 2,583 +// 17 4,180 +// 18 6,764 +// 19 10,945 +// 20 17,710 +// 21 28,656 +// 22 46,367 +// 23 75,024 +// 24 121,392 +// 25 196,417 +// 26 317,810 +// 27 514,228 +// 28 832,039 +// 29 1,346,268 +// 30 2,178,308 +// 31 3,524,577 +// 32 5,702,886 +// 33 9,227,464 +// 34 14,930,351 +// 35 24,157,816 +// 36 39,088,168 +// 37 63,245,985 +// 38 102,334,154 +// 39 165,580,140 +// 40 267,914,295 +// 41 433,494,436 +// 42 701,408,732 +// 43 1,134,903,169 +// 44 1,836,311,902 +// 45 2,971,215,072 +// +// E.g., if, in a particular instantiation, the maximum number of nodes in a tree instance is 1,000,000, the maximum depth should be 28. +// You pick 28 because MN(28) is 832,039, which is less than or equal to 1,000,000, and MN(29) is 1,346,268, which is strictly greater than 1,000,000. + +template <class Abstractor, unsigned maxDepth = 32, class BSet = AVLTreeDefaultBSet<maxDepth> > +class AVLTree { +public: + + typedef typename Abstractor::key key; + typedef typename Abstractor::handle handle; + typedef typename Abstractor::size size; + + enum SearchType { + EQUAL = 1, + LESS = 2, + GREATER = 4, + LESS_EQUAL = EQUAL | LESS, + GREATER_EQUAL = EQUAL | GREATER + }; + + + Abstractor& abstractor() { return abs; } + + inline handle insert(handle h); + + inline handle search(key k, SearchType st = EQUAL); + inline handle search_least(); + inline handle search_greatest(); + + inline handle remove(key k); + + inline handle subst(handle new_node); + + void purge() { abs.root = null(); } + + bool is_empty() { return abs.root == null(); } + + AVLTree() { abs.root = null(); } + + class Iterator { + public: + + // Initialize depth to invalid value, to indicate iterator is + // invalid. (Depth is zero-base.) + Iterator() { depth = ~0U; } + + void start_iter(AVLTree &tree, key k, SearchType st = EQUAL) + { + // Mask of high bit in an int. + const int MASK_HIGH_BIT = (int) ~ ((~ (unsigned) 0) >> 1); + + // Save the tree that we're going to iterate through in a + // member variable. + tree_ = &tree; + + int cmp, target_cmp; + handle h = tree_->abs.root; + unsigned d = 0; + + depth = ~0U; + + if (h == null()) + // Tree is empty. + return; + + if (st & LESS) + // Key can be greater than key of starting node. + target_cmp = 1; + else if (st & GREATER) + // Key can be less than key of starting node. + target_cmp = -1; + else + // Key must be same as key of starting node. + target_cmp = 0; + + for (;;) { + cmp = cmp_k_n(k, h); + if (cmp == 0) { + if (st & EQUAL) { + // Equal node was sought and found as starting node. + depth = d; + break; + } + cmp = -target_cmp; + } else if (target_cmp != 0) { + if (!((cmp ^ target_cmp) & MASK_HIGH_BIT)) { + // cmp and target_cmp are both negative or both positive. + depth = d; + } + } + h = cmp < 0 ? get_lt(h) : get_gt(h); + if (h == null()) + break; + branch[d] = cmp > 0; + path_h[d++] = h; + } + } + + void start_iter_least(AVLTree &tree) + { + tree_ = &tree; + + handle h = tree_->abs.root; + + depth = ~0U; + + branch.reset(); + + while (h != null()) { + if (depth != ~0U) + path_h[depth] = h; + depth++; + h = get_lt(h); + } + } + + void start_iter_greatest(AVLTree &tree) + { + tree_ = &tree; + + handle h = tree_->abs.root; + + depth = ~0U; + + branch.set(); + + while (h != null()) { + if (depth != ~0U) + path_h[depth] = h; + depth++; + h = get_gt(h); + } + } + + handle operator*() + { + if (depth == ~0U) + return null(); + + return depth == 0 ? tree_->abs.root : path_h[depth - 1]; + } + + void operator++() + { + if (depth != ~0U) { + handle h = get_gt(**this); + if (h == null()) { + do { + if (depth == 0) { + depth = ~0U; + break; + } + depth--; + } while (branch[depth]); + } else { + branch[depth] = true; + path_h[depth++] = h; + for (;;) { + h = get_lt(h); + if (h == null()) + break; + branch[depth] = false; + path_h[depth++] = h; + } + } + } + } + + void operator--() + { + if (depth != ~0U) { + handle h = get_lt(**this); + if (h == null()) + do { + if (depth == 0) { + depth = ~0U; + break; + } + depth--; + } while (!branch[depth]); + else { + branch[depth] = false; + path_h[depth++] = h; + for (;;) { + h = get_gt(h); + if (h == null()) + break; + branch[depth] = true; + path_h[depth++] = h; + } + } + } + } + + void operator++(int) { ++(*this); } + void operator--(int) { --(*this); } + + protected: + + // Tree being iterated over. + AVLTree *tree_; + + // Records a path into the tree. If branch[n] is true, indicates + // take greater branch from the nth node in the path, otherwise + // take the less branch. branch[0] gives branch from root, and + // so on. + BSet branch; + + // Zero-based depth of path into tree. + unsigned depth; + + // Handles of nodes in path from root to current node (returned by *). + handle path_h[maxDepth - 1]; + + int cmp_k_n(key k, handle h) { return tree_->abs.compare_key_node(k, h); } + int cmp_n_n(handle h1, handle h2) { return tree_->abs.compare_node_node(h1, h2); } + handle get_lt(handle h) { return tree_->abs.get_less(h); } + handle get_gt(handle h) { return tree_->abs.get_greater(h); } + handle null() { return tree_->abs.null(); } + }; + + template<typename fwd_iter> + bool build(fwd_iter p, size num_nodes) + { + if (num_nodes == 0) { + abs.root = null(); + return true; + } + + // Gives path to subtree being built. If branch[N] is false, branch + // less from the node at depth N, if true branch greater. + BSet branch; + + // If rem[N] is true, then for the current subtree at depth N, it's + // greater subtree has one more node than it's less subtree. + BSet rem; + + // Depth of root node of current subtree. + unsigned depth = 0; + + // Number of nodes in current subtree. + size num_sub = num_nodes; + + // The algorithm relies on a stack of nodes whose less subtree has + // been built, but whose right subtree has not yet been built. The + // stack is implemented as linked list. The nodes are linked + // together by having the "greater" handle of a node set to the + // next node in the list. "less_parent" is the handle of the first + // node in the list. + handle less_parent = null(); + + // h is root of current subtree, child is one of its children. + handle h, child; + + for (;;) { + while (num_sub > 2) { + // Subtract one for root of subtree. + num_sub--; + rem[depth] = !!(num_sub & 1); + branch[depth++] = false; + num_sub >>= 1; + } + + if (num_sub == 2) { + // Build a subtree with two nodes, slanting to greater. + // I arbitrarily chose to always have the extra node in the + // greater subtree when there is an odd number of nodes to + // split between the two subtrees. + + h = *p; + p++; + child = *p; + p++; + set_lt(child, null()); + set_gt(child, null()); + set_bf(child, 0); + set_gt(h, child); + set_lt(h, null()); + set_bf(h, 1); + } else { // num_sub == 1 + // Build a subtree with one node. + + h = *p; + p++; + set_lt(h, null()); + set_gt(h, null()); + set_bf(h, 0); + } + + while (depth) { + depth--; + if (!branch[depth]) + // We've completed a less subtree. + break; + + // We've completed a greater subtree, so attach it to + // its parent (that is less than it). We pop the parent + // off the stack of less parents. + child = h; + h = less_parent; + less_parent = get_gt(h); + set_gt(h, child); + // num_sub = 2 * (num_sub - rem[depth]) + rem[depth] + 1 + num_sub <<= 1; + num_sub += 1 - rem[depth]; + if (num_sub & (num_sub - 1)) + // num_sub is not a power of 2 + set_bf(h, 0); + else + // num_sub is a power of 2 + set_bf(h, 1); + } + + if (num_sub == num_nodes) + // We've completed the full tree. + break; + + // The subtree we've completed is the less subtree of the + // next node in the sequence. + + child = h; + h = *p; + p++; + set_lt(h, child); + + // Put h into stack of less parents. + set_gt(h, less_parent); + less_parent = h; + + // Proceed to creating greater than subtree of h. + branch[depth] = true; + num_sub += rem[depth++]; + + } // end for (;;) + + abs.root = h; + + return true; + } + +protected: + + friend class Iterator; + + // Create a class whose sole purpose is to take advantage of + // the "empty member" optimization. + struct abs_plus_root : public Abstractor { + // The handle of the root element in the AVL tree. + handle root; + }; + + abs_plus_root abs; + + + handle get_lt(handle h) { return abs.get_less(h); } + void set_lt(handle h, handle lh) { abs.set_less(h, lh); } + + handle get_gt(handle h) { return abs.get_greater(h); } + void set_gt(handle h, handle gh) { abs.set_greater(h, gh); } + + int get_bf(handle h) { return abs.get_balance_factor(h); } + void set_bf(handle h, int bf) { abs.set_balance_factor(h, bf); } + + int cmp_k_n(key k, handle h) { return abs.compare_key_node(k, h); } + int cmp_n_n(handle h1, handle h2) { return abs.compare_node_node(h1, h2); } + + handle null() { return abs.null(); } + +private: + + // Balances subtree, returns handle of root node of subtree + // after balancing. + handle balance(handle bal_h) + { + handle deep_h; + + // Either the "greater than" or the "less than" subtree of + // this node has to be 2 levels deeper (or else it wouldn't + // need balancing). + + if (get_bf(bal_h) > 0) { + // "Greater than" subtree is deeper. + + deep_h = get_gt(bal_h); + + if (get_bf(deep_h) < 0) { + handle old_h = bal_h; + bal_h = get_lt(deep_h); + + set_gt(old_h, get_lt(bal_h)); + set_lt(deep_h, get_gt(bal_h)); + set_lt(bal_h, old_h); + set_gt(bal_h, deep_h); + + int bf = get_bf(bal_h); + if (bf != 0) { + if (bf > 0) { + set_bf(old_h, -1); + set_bf(deep_h, 0); + } else { + set_bf(deep_h, 1); + set_bf(old_h, 0); + } + set_bf(bal_h, 0); + } else { + set_bf(old_h, 0); + set_bf(deep_h, 0); + } + } else { + set_gt(bal_h, get_lt(deep_h)); + set_lt(deep_h, bal_h); + if (get_bf(deep_h) == 0) { + set_bf(deep_h, -1); + set_bf(bal_h, 1); + } else { + set_bf(deep_h, 0); + set_bf(bal_h, 0); + } + bal_h = deep_h; + } + } else { + // "Less than" subtree is deeper. + + deep_h = get_lt(bal_h); + + if (get_bf(deep_h) > 0) { + handle old_h = bal_h; + bal_h = get_gt(deep_h); + set_lt(old_h, get_gt(bal_h)); + set_gt(deep_h, get_lt(bal_h)); + set_gt(bal_h, old_h); + set_lt(bal_h, deep_h); + + int bf = get_bf(bal_h); + if (bf != 0) { + if (bf < 0) { + set_bf(old_h, 1); + set_bf(deep_h, 0); + } else { + set_bf(deep_h, -1); + set_bf(old_h, 0); + } + set_bf(bal_h, 0); + } else { + set_bf(old_h, 0); + set_bf(deep_h, 0); + } + } else { + set_lt(bal_h, get_gt(deep_h)); + set_gt(deep_h, bal_h); + if (get_bf(deep_h) == 0) { + set_bf(deep_h, 1); + set_bf(bal_h, -1); + } else { + set_bf(deep_h, 0); + set_bf(bal_h, 0); + } + bal_h = deep_h; + } + } + + return bal_h; + } + +}; + +template <class Abstractor, unsigned maxDepth, class BSet> +inline typename AVLTree<Abstractor, maxDepth, BSet>::handle +AVLTree<Abstractor, maxDepth, BSet>::insert(handle h) +{ + set_lt(h, null()); + set_gt(h, null()); + set_bf(h, 0); + + if (abs.root == null()) + abs.root = h; + else { + // Last unbalanced node encountered in search for insertion point. + handle unbal = null(); + // Parent of last unbalanced node. + handle parent_unbal = null(); + // Balance factor of last unbalanced node. + int unbal_bf; + + // Zero-based depth in tree. + unsigned depth = 0, unbal_depth = 0; + + // Records a path into the tree. If branch[n] is true, indicates + // take greater branch from the nth node in the path, otherwise + // take the less branch. branch[0] gives branch from root, and + // so on. + BSet branch; + + handle hh = abs.root; + handle parent = null(); + int cmp; + + do { + if (get_bf(hh) != 0) { + unbal = hh; + parent_unbal = parent; + unbal_depth = depth; + } + cmp = cmp_n_n(h, hh); + if (cmp == 0) + // Duplicate key. + return hh; + parent = hh; + hh = cmp < 0 ? get_lt(hh) : get_gt(hh); + branch[depth++] = cmp > 0; + } while (hh != null()); + + // Add node to insert as leaf of tree. + if (cmp < 0) + set_lt(parent, h); + else + set_gt(parent, h); + + depth = unbal_depth; + + if (unbal == null()) + hh = abs.root; + else { + cmp = branch[depth++] ? 1 : -1; + unbal_bf = get_bf(unbal); + if (cmp < 0) + unbal_bf--; + else // cmp > 0 + unbal_bf++; + hh = cmp < 0 ? get_lt(unbal) : get_gt(unbal); + if ((unbal_bf != -2) && (unbal_bf != 2)) { + // No rebalancing of tree is necessary. + set_bf(unbal, unbal_bf); + unbal = null(); + } + } + + if (hh != null()) + while (h != hh) { + cmp = branch[depth++] ? 1 : -1; + if (cmp < 0) { + set_bf(hh, -1); + hh = get_lt(hh); + } else { // cmp > 0 + set_bf(hh, 1); + hh = get_gt(hh); + } + } + + if (unbal != null()) { + unbal = balance(unbal); + if (parent_unbal == null()) + abs.root = unbal; + else { + depth = unbal_depth - 1; + cmp = branch[depth] ? 1 : -1; + if (cmp < 0) + set_lt(parent_unbal, unbal); + else // cmp > 0 + set_gt(parent_unbal, unbal); + } + } + } + + return h; +} + +template <class Abstractor, unsigned maxDepth, class BSet> +inline typename AVLTree<Abstractor, maxDepth, BSet>::handle +AVLTree<Abstractor, maxDepth, BSet>::search(key k, typename AVLTree<Abstractor, maxDepth, BSet>::SearchType st) +{ + const int MASK_HIGH_BIT = (int) ~ ((~ (unsigned) 0) >> 1); + + int cmp, target_cmp; + handle match_h = null(); + handle h = abs.root; + + if (st & LESS) + target_cmp = 1; + else if (st & GREATER) + target_cmp = -1; + else + target_cmp = 0; + + while (h != null()) { + cmp = cmp_k_n(k, h); + if (cmp == 0) { + if (st & EQUAL) { + match_h = h; + break; + } + cmp = -target_cmp; + } else if (target_cmp != 0) + if (!((cmp ^ target_cmp) & MASK_HIGH_BIT)) + // cmp and target_cmp are both positive or both negative. + match_h = h; + h = cmp < 0 ? get_lt(h) : get_gt(h); + } + + return match_h; +} + +template <class Abstractor, unsigned maxDepth, class BSet> +inline typename AVLTree<Abstractor, maxDepth, BSet>::handle +AVLTree<Abstractor, maxDepth, BSet>::search_least() +{ + handle h = abs.root, parent = null(); + + while (h != null()) { + parent = h; + h = get_lt(h); + } + + return parent; +} + +template <class Abstractor, unsigned maxDepth, class BSet> +inline typename AVLTree<Abstractor, maxDepth, BSet>::handle +AVLTree<Abstractor, maxDepth, BSet>::search_greatest() +{ + handle h = abs.root, parent = null(); + + while (h != null()) { + parent = h; + h = get_gt(h); + } + + return parent; +} + +template <class Abstractor, unsigned maxDepth, class BSet> +inline typename AVLTree<Abstractor, maxDepth, BSet>::handle +AVLTree<Abstractor, maxDepth, BSet>::remove(key k) +{ + // Zero-based depth in tree. + unsigned depth = 0, rm_depth; + + // Records a path into the tree. If branch[n] is true, indicates + // take greater branch from the nth node in the path, otherwise + // take the less branch. branch[0] gives branch from root, and + // so on. + BSet branch; + + handle h = abs.root; + handle parent = null(), child; + int cmp, cmp_shortened_sub_with_path = 0; + + for (;;) { + if (h == null()) + // No node in tree with given key. + return null(); + cmp = cmp_k_n(k, h); + if (cmp == 0) + // Found node to remove. + break; + parent = h; + h = cmp < 0 ? get_lt(h) : get_gt(h); + branch[depth++] = cmp > 0; + cmp_shortened_sub_with_path = cmp; + } + handle rm = h; + handle parent_rm = parent; + rm_depth = depth; + + // If the node to remove is not a leaf node, we need to get a + // leaf node, or a node with a single leaf as its child, to put + // in the place of the node to remove. We will get the greatest + // node in the less subtree (of the node to remove), or the least + // node in the greater subtree. We take the leaf node from the + // deeper subtree, if there is one. + + if (get_bf(h) < 0) { + child = get_lt(h); + branch[depth] = false; + cmp = -1; + } else { + child = get_gt(h); + branch[depth] = true; + cmp = 1; + } + depth++; + + if (child != null()) { + cmp = -cmp; + do { + parent = h; + h = child; + if (cmp < 0) { + child = get_lt(h); + branch[depth] = false; + } else { + child = get_gt(h); + branch[depth] = true; + } + depth++; + } while (child != null()); + + if (parent == rm) + // Only went through do loop once. Deleted node will be replaced + // in the tree structure by one of its immediate children. + cmp_shortened_sub_with_path = -cmp; + else + cmp_shortened_sub_with_path = cmp; + + // Get the handle of the opposite child, which may not be null. + child = cmp > 0 ? get_lt(h) : get_gt(h); + } + + if (parent == null()) + // There were only 1 or 2 nodes in this tree. + abs.root = child; + else if (cmp_shortened_sub_with_path < 0) + set_lt(parent, child); + else + set_gt(parent, child); + + // "path" is the parent of the subtree being eliminated or reduced + // from a depth of 2 to 1. If "path" is the node to be removed, we + // set path to the node we're about to poke into the position of the + // node to be removed. + handle path = parent == rm ? h : parent; + + if (h != rm) { + // Poke in the replacement for the node to be removed. + set_lt(h, get_lt(rm)); + set_gt(h, get_gt(rm)); + set_bf(h, get_bf(rm)); + if (parent_rm == null()) + abs.root = h; + else { + depth = rm_depth - 1; + if (branch[depth]) + set_gt(parent_rm, h); + else + set_lt(parent_rm, h); + } + } + + if (path != null()) { + // Create a temporary linked list from the parent of the path node + // to the root node. + h = abs.root; + parent = null(); + depth = 0; + while (h != path) { + if (branch[depth++]) { + child = get_gt(h); + set_gt(h, parent); + } else { + child = get_lt(h); + set_lt(h, parent); + } + parent = h; + h = child; + } + + // Climb from the path node to the root node using the linked + // list, restoring the tree structure and rebalancing as necessary. + bool reduced_depth = true; + int bf; + cmp = cmp_shortened_sub_with_path; + for (;;) { + if (reduced_depth) { + bf = get_bf(h); + if (cmp < 0) + bf++; + else // cmp > 0 + bf--; + if ((bf == -2) || (bf == 2)) { + h = balance(h); + bf = get_bf(h); + } else + set_bf(h, bf); + reduced_depth = (bf == 0); + } + if (parent == null()) + break; + child = h; + h = parent; + cmp = branch[--depth] ? 1 : -1; + if (cmp < 0) { + parent = get_lt(h); + set_lt(h, child); + } else { + parent = get_gt(h); + set_gt(h, child); + } + } + abs.root = h; + } + + return rm; +} + +template <class Abstractor, unsigned maxDepth, class BSet> +inline typename AVLTree<Abstractor, maxDepth, BSet>::handle +AVLTree<Abstractor, maxDepth, BSet>::subst(handle new_node) +{ + handle h = abs.root; + handle parent = null(); + int cmp, last_cmp; + + /* Search for node already in tree with same key. */ + for (;;) { + if (h == null()) + /* No node in tree with same key as new node. */ + return null(); + cmp = cmp_n_n(new_node, h); + if (cmp == 0) + /* Found the node to substitute new one for. */ + break; + last_cmp = cmp; + parent = h; + h = cmp < 0 ? get_lt(h) : get_gt(h); + } + + /* Copy tree housekeeping fields from node in tree to new node. */ + set_lt(new_node, get_lt(h)); + set_gt(new_node, get_gt(h)); + set_bf(new_node, get_bf(h)); + + if (parent == null()) + /* New node is also new root. */ + abs.root = new_node; + else { + /* Make parent point to new node. */ + if (last_cmp < 0) + set_lt(parent, new_node); + else + set_gt(parent, new_node); + } + + return h; +} + +} + +#endif diff --git a/Source/JavaScriptCore/wtf/Alignment.h b/Source/JavaScriptCore/wtf/Alignment.h new file mode 100644 index 000000000..d42b307ed --- /dev/null +++ b/Source/JavaScriptCore/wtf/Alignment.h @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2005, 2006, 2007, 2008 Apple Inc. All rights reserved. + * + * This library 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 library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#ifndef WTF_Alignment_h +#define WTF_Alignment_h + +#include "Platform.h" +#include <algorithm> + +namespace WTF { + +#if COMPILER(GCC) || COMPILER(MINGW) || COMPILER(RVCT) || COMPILER(GCCE) + #define WTF_ALIGN_OF(type) __alignof__(type) + #define WTF_ALIGNED(variable_type, variable, n) variable_type variable __attribute__((__aligned__(n))) +#elif COMPILER(MSVC) + #define WTF_ALIGN_OF(type) __alignof(type) + #define WTF_ALIGNED(variable_type, variable, n) __declspec(align(n)) variable_type variable +#else + #error WTF_ALIGN macros need alignment control. +#endif + +#if COMPILER(GCC) && !COMPILER(INTEL) && (((__GNUC__ * 100) + __GNUC_MINOR__) >= 303) + typedef char __attribute__((__may_alias__)) AlignedBufferChar; +#else + typedef char AlignedBufferChar; +#endif + + template<size_t size, size_t alignment> struct AlignedBuffer; + template<size_t size> struct AlignedBuffer<size, 1> { AlignedBufferChar buffer[size]; }; + template<size_t size> struct AlignedBuffer<size, 2> { WTF_ALIGNED(AlignedBufferChar, buffer[size], 2); }; + template<size_t size> struct AlignedBuffer<size, 4> { WTF_ALIGNED(AlignedBufferChar, buffer[size], 4); }; + template<size_t size> struct AlignedBuffer<size, 8> { WTF_ALIGNED(AlignedBufferChar, buffer[size], 8); }; + template<size_t size> struct AlignedBuffer<size, 16> { WTF_ALIGNED(AlignedBufferChar, buffer[size], 16); }; + template<size_t size> struct AlignedBuffer<size, 32> { WTF_ALIGNED(AlignedBufferChar, buffer[size], 32); }; + template<size_t size> struct AlignedBuffer<size, 64> { WTF_ALIGNED(AlignedBufferChar, buffer[size], 64); }; + + template <size_t size, size_t alignment> + void swap(AlignedBuffer<size, alignment>& a, AlignedBuffer<size, alignment>& b) + { + for (size_t i = 0; i < size; ++i) + std::swap(a.buffer[i], b.buffer[i]); + } + +} + +#endif // WTF_Alignment_h diff --git a/Source/JavaScriptCore/wtf/AlwaysInline.h b/Source/JavaScriptCore/wtf/AlwaysInline.h new file mode 100644 index 000000000..de12ddd90 --- /dev/null +++ b/Source/JavaScriptCore/wtf/AlwaysInline.h @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2005, 2007, 2008 Apple Inc. All rights reserved. + * + * This library 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 library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +/* This file is no longer necessary, since all the functionality has been moved to Compiler.h. */ + +#include "Platform.h" diff --git a/Source/JavaScriptCore/wtf/ArrayBuffer.cpp b/Source/JavaScriptCore/wtf/ArrayBuffer.cpp new file mode 100644 index 000000000..45cfa1deb --- /dev/null +++ b/Source/JavaScriptCore/wtf/ArrayBuffer.cpp @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2009 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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 APPLE COMPUTER, INC. ``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 APPLE COMPUTER, INC. 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 "config.h" +#include "ArrayBuffer.h" + +#include "ArrayBufferView.h" + +#include <wtf/RefPtr.h> +#include <wtf/Vector.h> + +namespace WTF { + +bool ArrayBuffer::transfer(ArrayBufferContents& result, Vector<RefPtr<ArrayBufferView> >& neuteredViews) +{ + RefPtr<ArrayBuffer> keepAlive(this); + + if (!m_contents.m_data) { + result.m_data = 0; + return false; + } + + m_contents.transfer(result); + + while (m_firstView) { + ArrayBufferView* current = m_firstView; + removeView(current); + current->neuter(); + neuteredViews.append(current); + } + return true; +} + +void ArrayBuffer::addView(ArrayBufferView* view) +{ + view->m_buffer = this; + view->m_prevView = 0; + view->m_nextView = m_firstView; + if (m_firstView) + m_firstView->m_prevView = view; + m_firstView = view; +} + +void ArrayBuffer::removeView(ArrayBufferView* view) +{ + ASSERT(this == view->m_buffer); + if (view->m_nextView) + view->m_nextView->m_prevView = view->m_prevView; + if (view->m_prevView) + view->m_prevView->m_nextView = view->m_nextView; + if (m_firstView == view) + m_firstView = view->m_nextView; + view->m_prevView = view->m_nextView = 0; +} + +} diff --git a/Source/JavaScriptCore/wtf/ArrayBuffer.h b/Source/JavaScriptCore/wtf/ArrayBuffer.h new file mode 100644 index 000000000..ee95f5bc6 --- /dev/null +++ b/Source/JavaScriptCore/wtf/ArrayBuffer.h @@ -0,0 +1,220 @@ +/* + * Copyright (C) 2009 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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 APPLE COMPUTER, INC. ``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 APPLE COMPUTER, INC. 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. + */ + +#ifndef ArrayBuffer_h +#define ArrayBuffer_h + +#include <wtf/HashSet.h> +#include <wtf/PassRefPtr.h> +#include <wtf/RefCounted.h> +#include <wtf/Vector.h> + +namespace WTF { + +class ArrayBuffer; +class ArrayBufferView; + +class ArrayBufferContents { + WTF_MAKE_NONCOPYABLE(ArrayBufferContents); +public: + ArrayBufferContents() + : m_data(0) + , m_sizeInBytes(0) + { } + + inline ~ArrayBufferContents(); + + void* data() { return m_data; } + unsigned sizeInBytes() { return m_sizeInBytes; } + +private: + ArrayBufferContents(void* data, unsigned sizeInBytes) + : m_data(data) + , m_sizeInBytes(sizeInBytes) + { } + + friend class ArrayBuffer; + + static inline void tryAllocate(unsigned numElements, unsigned elementByteSize, ArrayBufferContents&); + void transfer(ArrayBufferContents& other) + { + ASSERT(!other.m_data); + other.m_data = m_data; + other.m_sizeInBytes = m_sizeInBytes; + m_data = 0; + m_sizeInBytes = 0; + } + + void* m_data; + unsigned m_sizeInBytes; +}; + +class ArrayBuffer : public RefCounted<ArrayBuffer> { +public: + static inline PassRefPtr<ArrayBuffer> create(unsigned numElements, unsigned elementByteSize); + static inline PassRefPtr<ArrayBuffer> create(ArrayBuffer*); + static inline PassRefPtr<ArrayBuffer> create(const void* source, unsigned byteLength); + static inline PassRefPtr<ArrayBuffer> create(ArrayBufferContents&); + + inline void* data(); + inline const void* data() const; + inline unsigned byteLength() const; + + inline PassRefPtr<ArrayBuffer> slice(int begin, int end) const; + inline PassRefPtr<ArrayBuffer> slice(int begin) const; + + void addView(ArrayBufferView*); + void removeView(ArrayBufferView*); + + bool transfer(ArrayBufferContents&, Vector<RefPtr<ArrayBufferView> >& neuteredViews); + bool isNeutered() { return !m_contents.m_data; } + + ~ArrayBuffer() { } + +private: + inline ArrayBuffer(ArrayBufferContents&); + inline PassRefPtr<ArrayBuffer> sliceImpl(unsigned begin, unsigned end) const; + inline unsigned clampIndex(int index) const; + static inline int clampValue(int x, int left, int right); + + ArrayBufferContents m_contents; + ArrayBufferView* m_firstView; +}; + +int ArrayBuffer::clampValue(int x, int left, int right) +{ + ASSERT(left <= right); + if (x < left) + x = left; + if (right < x) + x = right; + return x; +} + +PassRefPtr<ArrayBuffer> ArrayBuffer::create(unsigned numElements, unsigned elementByteSize) +{ + ArrayBufferContents contents; + ArrayBufferContents::tryAllocate(numElements, elementByteSize, contents); + if (!contents.m_data) + return 0; + return adoptRef(new ArrayBuffer(contents)); +} + +PassRefPtr<ArrayBuffer> ArrayBuffer::create(ArrayBuffer* other) +{ + return ArrayBuffer::create(other->data(), other->byteLength()); +} + +PassRefPtr<ArrayBuffer> ArrayBuffer::create(const void* source, unsigned byteLength) +{ + ArrayBufferContents contents; + ArrayBufferContents::tryAllocate(byteLength, 1, contents); + if (!contents.m_data) + return 0; + RefPtr<ArrayBuffer> buffer = adoptRef(new ArrayBuffer(contents)); + memcpy(buffer->data(), source, byteLength); + return buffer.release(); +} + +PassRefPtr<ArrayBuffer> ArrayBuffer::create(ArrayBufferContents& contents) +{ + return adoptRef(new ArrayBuffer(contents)); +} + +ArrayBuffer::ArrayBuffer(ArrayBufferContents& contents) + : m_firstView(0) +{ + contents.transfer(m_contents); +} + +void* ArrayBuffer::data() +{ + return m_contents.m_data; +} + +const void* ArrayBuffer::data() const +{ + return m_contents.m_data; +} + +unsigned ArrayBuffer::byteLength() const +{ + return m_contents.m_sizeInBytes; +} + +PassRefPtr<ArrayBuffer> ArrayBuffer::slice(int begin, int end) const +{ + return sliceImpl(clampIndex(begin), clampIndex(end)); +} + +PassRefPtr<ArrayBuffer> ArrayBuffer::slice(int begin) const +{ + return sliceImpl(clampIndex(begin), byteLength()); +} + +PassRefPtr<ArrayBuffer> ArrayBuffer::sliceImpl(unsigned begin, unsigned end) const +{ + unsigned size = begin <= end ? end - begin : 0; + return ArrayBuffer::create(static_cast<const char*>(data()) + begin, size); +} + +unsigned ArrayBuffer::clampIndex(int index) const +{ + unsigned currentLength = byteLength(); + if (index < 0) + index = currentLength + index; + return clampValue(index, 0, currentLength); +} + +void ArrayBufferContents::tryAllocate(unsigned numElements, unsigned elementByteSize, ArrayBufferContents& result) +{ + // Do not allow 32-bit overflow of the total size. + // FIXME: Why not? The tryFastCalloc function already checks its arguments, + // and will fail if there is any overflow, so why should we include a + // redudant unnecessarily restrictive check here? + if (numElements) { + unsigned totalSize = numElements * elementByteSize; + if (totalSize / numElements != elementByteSize) { + result.m_data = 0; + return; + } + } + if (WTF::tryFastCalloc(numElements, elementByteSize).getValue(result.m_data)) { + result.m_sizeInBytes = numElements * elementByteSize; + return; + } + result.m_data = 0; +} + +ArrayBufferContents::~ArrayBufferContents() +{ + WTF::fastFree(m_data); +} + +} // namespace WTF + +using WTF::ArrayBuffer; + +#endif // ArrayBuffer_h diff --git a/Source/JavaScriptCore/wtf/ArrayBufferView.cpp b/Source/JavaScriptCore/wtf/ArrayBufferView.cpp new file mode 100644 index 000000000..67dbdcf8c --- /dev/null +++ b/Source/JavaScriptCore/wtf/ArrayBufferView.cpp @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2009 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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 APPLE COMPUTER, INC. ``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 APPLE COMPUTER, INC. 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 "config.h" +#include "ArrayBufferView.h" + +#include "ArrayBuffer.h" + +namespace WTF { + +ArrayBufferView::ArrayBufferView(PassRefPtr<ArrayBuffer> buffer, + unsigned byteOffset) + : m_byteOffset(byteOffset) + , m_buffer(buffer) +{ + m_baseAddress = m_buffer ? (static_cast<char*>(m_buffer->data()) + m_byteOffset) : 0; + if (m_buffer) + m_buffer->addView(this); +} + +ArrayBufferView::~ArrayBufferView() +{ + if (m_buffer) + m_buffer->removeView(this); +} + +void ArrayBufferView::neuter() +{ + m_buffer = 0; + m_byteOffset = 0; +} + +} diff --git a/Source/JavaScriptCore/wtf/ArrayBufferView.h b/Source/JavaScriptCore/wtf/ArrayBufferView.h new file mode 100644 index 000000000..ccb7fd348 --- /dev/null +++ b/Source/JavaScriptCore/wtf/ArrayBufferView.h @@ -0,0 +1,198 @@ +/* + * Copyright (C) 2009 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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 APPLE COMPUTER, INC. ``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 APPLE COMPUTER, INC. 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. + */ + +#ifndef ArrayBufferView_h +#define ArrayBufferView_h + +#include "ArrayBuffer.h" + +#include <algorithm> +#include <limits.h> +#include <wtf/PassRefPtr.h> +#include <wtf/RefCounted.h> +#include <wtf/RefPtr.h> + +namespace WTF { + +class ArrayBufferView : public RefCounted<ArrayBufferView> { + public: + virtual bool isByteArray() const { return false; } + virtual bool isUnsignedByteArray() const { return false; } + virtual bool isShortArray() const { return false; } + virtual bool isUnsignedShortArray() const { return false; } + virtual bool isIntArray() const { return false; } + virtual bool isUnsignedIntArray() const { return false; } + virtual bool isFloatArray() const { return false; } + virtual bool isDoubleArray() const { return false; } + virtual bool isDataView() const { return false; } + + PassRefPtr<ArrayBuffer> buffer() const + { + return m_buffer; + } + + void* baseAddress() const + { + return m_baseAddress; + } + + unsigned byteOffset() const + { + return m_byteOffset; + } + + virtual unsigned byteLength() const = 0; + + virtual ~ArrayBufferView(); + + protected: + ArrayBufferView(PassRefPtr<ArrayBuffer>, unsigned byteOffset); + + inline bool setImpl(ArrayBufferView*, unsigned byteOffset); + + inline bool setRangeImpl(const char* data, size_t dataByteLength, unsigned byteOffset); + + inline bool zeroRangeImpl(unsigned byteOffset, size_t rangeByteLength); + + static inline void calculateOffsetAndLength(int start, int end, unsigned arraySize, + unsigned* offset, unsigned* length); + + // Helper to verify that a given sub-range of an ArrayBuffer is + // within range. + template <typename T> + static bool verifySubRange(PassRefPtr<ArrayBuffer> buffer, + unsigned byteOffset, + unsigned numElements) + { + if (!buffer) + return false; + if (sizeof(T) > 1 && byteOffset % sizeof(T)) + return false; + if (byteOffset > buffer->byteLength()) + return false; + unsigned remainingElements = (buffer->byteLength() - byteOffset) / sizeof(T); + if (numElements > remainingElements) + return false; + return true; + } + + // Input offset is in number of elements from this array's view; + // output offset is in number of bytes from the underlying buffer's view. + template <typename T> + static void clampOffsetAndNumElements(PassRefPtr<ArrayBuffer> buffer, + unsigned arrayByteOffset, + unsigned *offset, + unsigned *numElements) + { + unsigned maxOffset = (UINT_MAX - arrayByteOffset) / sizeof(T); + if (*offset > maxOffset) { + *offset = buffer->byteLength(); + *numElements = 0; + return; + } + *offset = arrayByteOffset + *offset * sizeof(T); + *offset = std::min(buffer->byteLength(), *offset); + unsigned remainingElements = (buffer->byteLength() - *offset) / sizeof(T); + *numElements = std::min(remainingElements, *numElements); + } + + virtual void neuter(); + + // This is the address of the ArrayBuffer's storage, plus the byte offset. + void* m_baseAddress; + + unsigned m_byteOffset; + + private: + friend class ArrayBuffer; + RefPtr<ArrayBuffer> m_buffer; + ArrayBufferView* m_prevView; + ArrayBufferView* m_nextView; +}; + +bool ArrayBufferView::setImpl(ArrayBufferView* array, unsigned byteOffset) +{ + if (byteOffset > byteLength() + || byteOffset + array->byteLength() > byteLength() + || byteOffset + array->byteLength() < byteOffset) { + // Out of range offset or overflow + return false; + } + + char* base = static_cast<char*>(baseAddress()); + memmove(base + byteOffset, array->baseAddress(), array->byteLength()); + return true; +} + +bool ArrayBufferView::setRangeImpl(const char* data, size_t dataByteLength, unsigned byteOffset) +{ + if (byteOffset > byteLength() + || byteOffset + dataByteLength > byteLength() + || byteOffset + dataByteLength < byteOffset) { + // Out of range offset or overflow + return false; + } + + char* base = static_cast<char*>(baseAddress()); + memmove(base + byteOffset, data, dataByteLength); + return true; +} + +bool ArrayBufferView::zeroRangeImpl(unsigned byteOffset, size_t rangeByteLength) +{ + if (byteOffset > byteLength() + || byteOffset + rangeByteLength > byteLength() + || byteOffset + rangeByteLength < byteOffset) { + // Out of range offset or overflow + return false; + } + + char* base = static_cast<char*>(baseAddress()); + memset(base + byteOffset, 0, rangeByteLength); + return true; +} + +void ArrayBufferView::calculateOffsetAndLength(int start, int end, unsigned arraySize, + unsigned* offset, unsigned* length) +{ + if (start < 0) + start += arraySize; + if (start < 0) + start = 0; + if (end < 0) + end += arraySize; + if (end < 0) + end = 0; + if (end < start) + end = start; + *offset = static_cast<unsigned>(start); + *length = static_cast<unsigned>(end - start); +} + +} // namespace WTF + +using WTF::ArrayBufferView; + +#endif // ArrayBufferView_h diff --git a/Source/JavaScriptCore/wtf/Assertions.cpp b/Source/JavaScriptCore/wtf/Assertions.cpp new file mode 100644 index 000000000..de062ce25 --- /dev/null +++ b/Source/JavaScriptCore/wtf/Assertions.cpp @@ -0,0 +1,320 @@ +/* + * Copyright (C) 2003, 2006, 2007 Apple Inc. All rights reserved. + * Copyright (C) 2007-2009 Torch Mobile, Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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 APPLE COMPUTER, INC. ``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 APPLE COMPUTER, INC. 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. + */ + +// The vprintf_stderr_common function triggers this error in the Mac build. +// Feel free to remove this pragma if this file builds on Mac. +// According to http://gcc.gnu.org/onlinedocs/gcc-4.2.1/gcc/Diagnostic-Pragmas.html#Diagnostic-Pragmas +// we need to place this directive before any data or functions are defined. +#pragma GCC diagnostic ignored "-Wmissing-format-attribute" + +#include "config.h" +#include "Assertions.h" + +#include "OwnArrayPtr.h" + +#include <stdio.h> +#include <stdarg.h> +#include <string.h> + +#if PLATFORM(MAC) +#include <CoreFoundation/CFString.h> +#include <asl.h> +#endif + +#if COMPILER(MSVC) && !OS(WINCE) +#include <crtdbg.h> +#endif + +#if OS(WINDOWS) +#include <windows.h> +#endif + +#if OS(DARWIN) || OS(LINUX) +#include <cxxabi.h> +#include <dlfcn.h> +#include <execinfo.h> +#endif + +#if PLATFORM(BLACKBERRY) +#include <BlackBerryPlatformLog.h> +#endif + +extern "C" { + +WTF_ATTRIBUTE_PRINTF(1, 0) +static void vprintf_stderr_common(const char* format, va_list args) +{ +#if PLATFORM(MAC) + if (strstr(format, "%@")) { + CFStringRef cfFormat = CFStringCreateWithCString(NULL, format, kCFStringEncodingUTF8); + CFStringRef str = CFStringCreateWithFormatAndArguments(NULL, NULL, cfFormat, args); + + int length = CFStringGetMaximumSizeForEncoding(CFStringGetLength(str), kCFStringEncodingUTF8); + char* buffer = (char*)malloc(length + 1); + + CFStringGetCString(str, buffer, length, kCFStringEncodingUTF8); + +#if !defined(BUILDING_ON_SNOW_LEOPARD) && !defined(BUILDING_ON_LION) + asl_log(0, 0, ASL_LEVEL_NOTICE, "%s", buffer); +#endif + fputs(buffer, stderr); + + free(buffer); + CFRelease(str); + CFRelease(cfFormat); + return; + } + +#if !defined(BUILDING_ON_SNOW_LEOPARD) && !defined(BUILDING_ON_LION) + va_list copyOfArgs; + va_copy(copyOfArgs, args); + asl_vlog(0, 0, ASL_LEVEL_NOTICE, format, copyOfArgs); + va_end(copyOfArgs); +#endif + + // Fall through to write to stderr in the same manner as other platforms. + +#elif PLATFORM(BLACKBERRY) + BlackBerry::Platform::logV(BlackBerry::Platform::LogLevelInfo, format, args); +#elif HAVE(ISDEBUGGERPRESENT) + if (IsDebuggerPresent()) { + size_t size = 1024; + + do { + char* buffer = (char*)malloc(size); + + if (buffer == NULL) + break; + + if (_vsnprintf(buffer, size, format, args) != -1) { +#if OS(WINCE) + // WinCE only supports wide chars + wchar_t* wideBuffer = (wchar_t*)malloc(size * sizeof(wchar_t)); + if (wideBuffer == NULL) + break; + for (unsigned int i = 0; i < size; ++i) { + if (!(wideBuffer[i] = buffer[i])) + break; + } + OutputDebugStringW(wideBuffer); + free(wideBuffer); +#else + OutputDebugStringA(buffer); +#endif + free(buffer); + break; + } + + free(buffer); + size *= 2; + } while (size > 1024); + } +#endif + vfprintf(stderr, format, args); +} + +#if COMPILER(CLANG) || (COMPILER(GCC) && GCC_VERSION_AT_LEAST(4, 6, 0)) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wformat-nonliteral" +#endif + +static void vprintf_stderr_with_prefix(const char* prefix, const char* format, va_list args) +{ + size_t prefixLength = strlen(prefix); + size_t formatLength = strlen(format); + OwnArrayPtr<char> formatWithPrefix = adoptArrayPtr(new char[prefixLength + formatLength + 1]); + memcpy(formatWithPrefix.get(), prefix, prefixLength); + memcpy(formatWithPrefix.get() + prefixLength, format, formatLength); + formatWithPrefix[prefixLength + formatLength] = 0; + + vprintf_stderr_common(formatWithPrefix.get(), args); +} + +static void vprintf_stderr_with_trailing_newline(const char* format, va_list args) +{ + size_t formatLength = strlen(format); + if (formatLength && format[formatLength - 1] == '\n') { + vprintf_stderr_common(format, args); + return; + } + + OwnArrayPtr<char> formatWithNewline = adoptArrayPtr(new char[formatLength + 2]); + memcpy(formatWithNewline.get(), format, formatLength); + formatWithNewline[formatLength] = '\n'; + formatWithNewline[formatLength + 1] = 0; + + vprintf_stderr_common(formatWithNewline.get(), args); +} + +#if COMPILER(CLANG) || (COMPILER(GCC) && GCC_VERSION_AT_LEAST(4, 6, 0)) +#pragma GCC diagnostic pop +#endif + +WTF_ATTRIBUTE_PRINTF(1, 2) +static void printf_stderr_common(const char* format, ...) +{ + va_list args; + va_start(args, format); + vprintf_stderr_common(format, args); + va_end(args); +} + +static void printCallSite(const char* file, int line, const char* function) +{ +#if OS(WINDOWS) && !OS(WINCE) && defined(_DEBUG) + _CrtDbgReport(_CRT_WARN, file, line, NULL, "%s\n", function); +#else + // By using this format, which matches the format used by MSVC for compiler errors, developers + // using Visual Studio can double-click the file/line number in the Output Window to have the + // editor navigate to that line of code. It seems fine for other developers, too. + printf_stderr_common("%s(%d) : %s\n", file, line, function); +#endif +} + +void WTFReportAssertionFailure(const char* file, int line, const char* function, const char* assertion) +{ + if (assertion) + printf_stderr_common("ASSERTION FAILED: %s\n", assertion); + else + printf_stderr_common("SHOULD NEVER BE REACHED\n"); + printCallSite(file, line, function); +} + +void WTFReportAssertionFailureWithMessage(const char* file, int line, const char* function, const char* assertion, const char* format, ...) +{ + va_list args; + va_start(args, format); + vprintf_stderr_with_prefix("ASSERTION FAILED: ", format, args); + va_end(args); + printf_stderr_common("\n%s\n", assertion); + printCallSite(file, line, function); +} + +void WTFReportArgumentAssertionFailure(const char* file, int line, const char* function, const char* argName, const char* assertion) +{ + printf_stderr_common("ARGUMENT BAD: %s, %s\n", argName, assertion); + printCallSite(file, line, function); +} + +void WTFGetBacktrace(void** stack, int* size) +{ +#if OS(DARWIN) || OS(LINUX) + *size = backtrace(stack, *size); +#elif OS(WINDOWS) && !OS(WINCE) + // The CaptureStackBackTrace function is available in XP, but it is not defined + // in the Windows Server 2003 R2 Platform SDK. So, we'll grab the function + // through GetProcAddress. + typedef WORD (NTAPI* RtlCaptureStackBackTraceFunc)(DWORD, DWORD, PVOID*, PDWORD); + HMODULE kernel32 = ::GetModuleHandleW(L"Kernel32.dll"); + if (!kernel32) { + *size = 0; + return; + } + RtlCaptureStackBackTraceFunc captureStackBackTraceFunc = reinterpret_cast<RtlCaptureStackBackTraceFunc>( + ::GetProcAddress(kernel32, "RtlCaptureStackBackTrace")); + if (captureStackBackTraceFunc) + *size = captureStackBackTraceFunc(0, *size, stack, 0); + else + *size = 0; +#else + *size = 0; +#endif +} + +void WTFReportBacktrace() +{ + static const int framesToShow = 31; + static const int framesToSkip = 2; + void* samples[framesToShow + framesToSkip]; + int frames = framesToShow + framesToSkip; + + WTFGetBacktrace(samples, &frames); + + for (int i = framesToSkip; i < frames; ++i) { + const char* mangledName = 0; + char* cxaDemangled = 0; + +#if !PLATFORM(GTK) && !PLATFORM(QT) && (OS(DARWIN) || OS(LINUX)) + Dl_info info; + if (dladdr(samples[i], &info) && info.dli_sname) + mangledName = info.dli_sname; + if (mangledName) + cxaDemangled = abi::__cxa_demangle(mangledName, 0, 0, 0); +#endif + const int frameNumber = i - framesToSkip + 1; + if (mangledName || cxaDemangled) + printf_stderr_common("%-3d %p %s\n", frameNumber, samples[i], cxaDemangled ? cxaDemangled : mangledName); + else + printf_stderr_common("%-3d %p\n", frameNumber, samples[i]); + free(cxaDemangled); + } +} + +void WTFReportFatalError(const char* file, int line, const char* function, const char* format, ...) +{ + va_list args; + va_start(args, format); + vprintf_stderr_with_prefix("FATAL ERROR: ", format, args); + va_end(args); + printf_stderr_common("\n"); + printCallSite(file, line, function); +} + +void WTFReportError(const char* file, int line, const char* function, const char* format, ...) +{ + va_list args; + va_start(args, format); + vprintf_stderr_with_prefix("ERROR: ", format, args); + va_end(args); + printf_stderr_common("\n"); + printCallSite(file, line, function); +} + +void WTFLog(WTFLogChannel* channel, const char* format, ...) +{ + if (channel->state != WTFLogChannelOn) + return; + + va_list args; + va_start(args, format); + vprintf_stderr_with_trailing_newline(format, args); + va_end(args); +} + +void WTFLogVerbose(const char* file, int line, const char* function, WTFLogChannel* channel, const char* format, ...) +{ + if (channel->state != WTFLogChannelOn) + return; + + va_list args; + va_start(args, format); + vprintf_stderr_with_trailing_newline(format, args); + va_end(args); + + printCallSite(file, line, function); +} + +} // extern "C" diff --git a/Source/JavaScriptCore/wtf/Assertions.h b/Source/JavaScriptCore/wtf/Assertions.h new file mode 100644 index 000000000..2b3d794a4 --- /dev/null +++ b/Source/JavaScriptCore/wtf/Assertions.h @@ -0,0 +1,392 @@ +/* + * Copyright (C) 2003, 2006, 2007 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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 APPLE COMPUTER, INC. ``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 APPLE COMPUTER, INC. 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. + */ + +#ifndef WTF_Assertions_h +#define WTF_Assertions_h + +/* + no namespaces because this file has to be includable from C and Objective-C + + Note, this file uses many GCC extensions, but it should be compatible with + C, Objective C, C++, and Objective C++. + + For non-debug builds, everything is disabled by default. + Defining any of the symbols explicitly prevents this from having any effect. + + MSVC7 note: variadic macro support was added in MSVC8, so for now we disable + those macros in MSVC7. For more info, see the MSDN document on variadic + macros here: + + http://msdn2.microsoft.com/en-us/library/ms177415(VS.80).aspx +*/ + +#include "Platform.h" + +#include <stddef.h> + +#if !COMPILER(MSVC) +#include <inttypes.h> +#endif + +#ifdef NDEBUG +/* Disable ASSERT* macros in release mode. */ +#define ASSERTIONS_DISABLED_DEFAULT 1 +#else +#define ASSERTIONS_DISABLED_DEFAULT 0 +#endif + +#if COMPILER(MSVC7_OR_LOWER) +#define HAVE_VARIADIC_MACRO 0 +#else +#define HAVE_VARIADIC_MACRO 1 +#endif + +#ifndef BACKTRACE_DISABLED +#define BACKTRACE_DISABLED ASSERTIONS_DISABLED_DEFAULT +#endif + +#ifndef ASSERT_DISABLED +#define ASSERT_DISABLED ASSERTIONS_DISABLED_DEFAULT +#endif + +#ifndef ASSERT_MSG_DISABLED +#if HAVE(VARIADIC_MACRO) +#define ASSERT_MSG_DISABLED ASSERTIONS_DISABLED_DEFAULT +#else +#define ASSERT_MSG_DISABLED 1 +#endif +#endif + +#ifndef ASSERT_ARG_DISABLED +#define ASSERT_ARG_DISABLED ASSERTIONS_DISABLED_DEFAULT +#endif + +#ifndef FATAL_DISABLED +#if HAVE(VARIADIC_MACRO) +#define FATAL_DISABLED ASSERTIONS_DISABLED_DEFAULT +#else +#define FATAL_DISABLED 1 +#endif +#endif + +#ifndef ERROR_DISABLED +#if HAVE(VARIADIC_MACRO) +#define ERROR_DISABLED ASSERTIONS_DISABLED_DEFAULT +#else +#define ERROR_DISABLED 1 +#endif +#endif + +#ifndef LOG_DISABLED +#if HAVE(VARIADIC_MACRO) +#define LOG_DISABLED ASSERTIONS_DISABLED_DEFAULT +#else +#define LOG_DISABLED 1 +#endif +#endif + +#if COMPILER(GCC) +#define WTF_PRETTY_FUNCTION __PRETTY_FUNCTION__ +#else +#define WTF_PRETTY_FUNCTION __FUNCTION__ +#endif + +/* WTF logging functions can process %@ in the format string to log a NSObject* but the printf format attribute + emits a warning when %@ is used in the format string. Until <rdar://problem/5195437> is resolved we can't include + the attribute when being used from Objective-C code in case it decides to use %@. */ +#if COMPILER(GCC) && !defined(__OBJC__) +#define WTF_ATTRIBUTE_PRINTF(formatStringArgument, extraArguments) __attribute__((__format__(printf, formatStringArgument, extraArguments))) +#else +#define WTF_ATTRIBUTE_PRINTF(formatStringArgument, extraArguments) +#endif + +/* These helper functions are always declared, but not necessarily always defined if the corresponding function is disabled. */ + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum { WTFLogChannelOff, WTFLogChannelOn } WTFLogChannelState; + +typedef struct { + unsigned mask; + const char *defaultName; + WTFLogChannelState state; +} WTFLogChannel; + +WTF_EXPORT_PRIVATE void WTFReportAssertionFailure(const char* file, int line, const char* function, const char* assertion); +WTF_EXPORT_PRIVATE void WTFReportAssertionFailureWithMessage(const char* file, int line, const char* function, const char* assertion, const char* format, ...) WTF_ATTRIBUTE_PRINTF(5, 6); +WTF_EXPORT_PRIVATE void WTFReportArgumentAssertionFailure(const char* file, int line, const char* function, const char* argName, const char* assertion); +WTF_EXPORT_PRIVATE void WTFReportFatalError(const char* file, int line, const char* function, const char* format, ...) WTF_ATTRIBUTE_PRINTF(4, 5); +WTF_EXPORT_PRIVATE void WTFReportError(const char* file, int line, const char* function, const char* format, ...) WTF_ATTRIBUTE_PRINTF(4, 5); +WTF_EXPORT_PRIVATE void WTFLog(WTFLogChannel*, const char* format, ...) WTF_ATTRIBUTE_PRINTF(2, 3); +WTF_EXPORT_PRIVATE void WTFLogVerbose(const char* file, int line, const char* function, WTFLogChannel*, const char* format, ...) WTF_ATTRIBUTE_PRINTF(5, 6); + +WTF_EXPORT_PRIVATE void WTFGetBacktrace(void** stack, int* size); +WTF_EXPORT_PRIVATE void WTFReportBacktrace(); + +#ifdef __cplusplus +} +#endif + +/* CRASH() - Raises a fatal error resulting in program termination and triggering either the debugger or the crash reporter. + + Use CRASH() in response to known, unrecoverable errors like out-of-memory. + Macro is enabled in both debug and release mode. + To test for unknown errors and verify assumptions, use ASSERT instead, to avoid impacting performance in release builds. + + Signals are ignored by the crash reporter on OS X so we must do better. +*/ +#ifndef CRASH +#if COMPILER(CLANG) +#define CRASH() do { \ + WTFReportBacktrace(); \ + *(int *)(uintptr_t)0xbbadbeef = 0; \ + __builtin_trap(); \ +} while (false) +#else +#define CRASH() do { \ + WTFReportBacktrace(); \ + *(int *)(uintptr_t)0xbbadbeef = 0; \ + ((void(*)())0)(); /* More reliable, but doesn't say BBADBEEF */ \ +} while (false) +#endif +#endif + +#if COMPILER(CLANG) +#define NO_RETURN_DUE_TO_CRASH NO_RETURN +#else +#define NO_RETURN_DUE_TO_CRASH +#endif + + +/* BACKTRACE + + Print a backtrace to the same location as ASSERT messages. +*/ + +#if BACKTRACE_DISABLED + +#define BACKTRACE() ((void)0) + +#else + +#define BACKTRACE() do { \ + WTFReportBacktrace(); \ +} while(false) + +#endif + +/* ASSERT, ASSERT_NOT_REACHED, ASSERT_UNUSED + + These macros are compiled out of release builds. + Expressions inside them are evaluated in debug builds only. +*/ + +#if OS(WINCE) && !PLATFORM(TORCHMOBILE) +/* FIXME: We include this here only to avoid a conflict with the ASSERT macro. */ +#include <windows.h> +#undef min +#undef max +#undef ERROR +#endif + +#if OS(WINDOWS) +/* FIXME: Change to use something other than ASSERT to avoid this conflict with the underlying platform */ +#undef ASSERT +#endif + +#if ASSERT_DISABLED + +#define ASSERT(assertion) ((void)0) +#define ASSERT_AT(assertion, file, line, function) ((void)0) +#define ASSERT_NOT_REACHED() ((void)0) +#define NO_RETURN_DUE_TO_ASSERT + +#if COMPILER(INTEL) && !OS(WINDOWS) || COMPILER(RVCT) +template<typename T> +inline void assertUnused(T& x) { (void)x; } +#define ASSERT_UNUSED(variable, assertion) (assertUnused(variable)) +#else +#define ASSERT_UNUSED(variable, assertion) ((void)variable) +#endif + +#else + +#define ASSERT(assertion) do \ + if (!(assertion)) { \ + WTFReportAssertionFailure(__FILE__, __LINE__, WTF_PRETTY_FUNCTION, #assertion); \ + CRASH(); \ + } \ +while (0) + +#define ASSERT_AT(assertion, file, line, function) do \ + if (!(assertion)) { \ + WTFReportAssertionFailure(file, line, function, #assertion); \ + CRASH(); \ + } \ +while (0) + +#define ASSERT_NOT_REACHED() do { \ + WTFReportAssertionFailure(__FILE__, __LINE__, WTF_PRETTY_FUNCTION, 0); \ + CRASH(); \ +} while (0) + +#define ASSERT_UNUSED(variable, assertion) ASSERT(assertion) + +#define NO_RETURN_DUE_TO_ASSERT NO_RETURN_DUE_TO_CRASH + +#endif + +/* ASSERT_WITH_MESSAGE */ + +#if COMPILER(MSVC7_OR_LOWER) +#define ASSERT_WITH_MESSAGE(assertion) ((void)0) +#elif ASSERT_MSG_DISABLED +#define ASSERT_WITH_MESSAGE(assertion, ...) ((void)0) +#else +#define ASSERT_WITH_MESSAGE(assertion, ...) do \ + if (!(assertion)) { \ + WTFReportAssertionFailureWithMessage(__FILE__, __LINE__, WTF_PRETTY_FUNCTION, #assertion, __VA_ARGS__); \ + CRASH(); \ + } \ +while (0) +#endif + +/* ASSERT_WITH_MESSAGE_UNUSED */ + +#if COMPILER(MSVC7_OR_LOWER) +#define ASSERT_WITH_MESSAGE_UNUSED(variable, assertion) ((void)0) +#elif ASSERT_MSG_DISABLED +#if COMPILER(INTEL) && !OS(WINDOWS) || COMPILER(RVCT) +template<typename T> +inline void assertWithMessageUnused(T& x) { (void)x; } +#define ASSERT_WITH_MESSAGE_UNUSED(variable, assertion, ...) (assertWithMessageUnused(variable)) +#else +#define ASSERT_WITH_MESSAGE_UNUSED(variable, assertion, ...) ((void)variable) +#endif +#else +#define ASSERT_WITH_MESSAGE_UNUSED(variable, assertion, ...) do \ + if (!(assertion)) { \ + WTFReportAssertionFailureWithMessage(__FILE__, __LINE__, WTF_PRETTY_FUNCTION, #assertion, __VA_ARGS__); \ + CRASH(); \ + } \ +while (0) +#endif + + +/* ASSERT_ARG */ + +#if ASSERT_ARG_DISABLED + +#define ASSERT_ARG(argName, assertion) ((void)0) + +#else + +#define ASSERT_ARG(argName, assertion) do \ + if (!(assertion)) { \ + WTFReportArgumentAssertionFailure(__FILE__, __LINE__, WTF_PRETTY_FUNCTION, #argName, #assertion); \ + CRASH(); \ + } \ +while (0) + +#endif + +/* COMPILE_ASSERT */ +#ifndef COMPILE_ASSERT +#define COMPILE_ASSERT(exp, name) typedef int dummy##name [(exp) ? 1 : -1] +#endif + +/* FATAL */ + +#if COMPILER(MSVC7_OR_LOWER) +#define FATAL() ((void)0) +#elif FATAL_DISABLED +#define FATAL(...) ((void)0) +#else +#define FATAL(...) do { \ + WTFReportFatalError(__FILE__, __LINE__, WTF_PRETTY_FUNCTION, __VA_ARGS__); \ + CRASH(); \ +} while (0) +#endif + +/* LOG_ERROR */ + +#if COMPILER(MSVC7_OR_LOWER) +#define LOG_ERROR() ((void)0) +#elif ERROR_DISABLED +#define LOG_ERROR(...) ((void)0) +#else +#define LOG_ERROR(...) WTFReportError(__FILE__, __LINE__, WTF_PRETTY_FUNCTION, __VA_ARGS__) +#endif + +/* LOG */ + +#if COMPILER(MSVC7_OR_LOWER) +#define LOG() ((void)0) +#elif LOG_DISABLED +#define LOG(channel, ...) ((void)0) +#else +#define LOG(channel, ...) WTFLog(&JOIN_LOG_CHANNEL_WITH_PREFIX(LOG_CHANNEL_PREFIX, channel), __VA_ARGS__) +#define JOIN_LOG_CHANNEL_WITH_PREFIX(prefix, channel) JOIN_LOG_CHANNEL_WITH_PREFIX_LEVEL_2(prefix, channel) +#define JOIN_LOG_CHANNEL_WITH_PREFIX_LEVEL_2(prefix, channel) prefix ## channel +#endif + +/* LOG_VERBOSE */ + +#if COMPILER(MSVC7_OR_LOWER) +#define LOG_VERBOSE(channel) ((void)0) +#elif LOG_DISABLED +#define LOG_VERBOSE(channel, ...) ((void)0) +#else +#define LOG_VERBOSE(channel, ...) WTFLogVerbose(__FILE__, __LINE__, WTF_PRETTY_FUNCTION, &JOIN_LOG_CHANNEL_WITH_PREFIX(LOG_CHANNEL_PREFIX, channel), __VA_ARGS__) +#endif + +#if ENABLE(GC_VALIDATION) +#define ASSERT_GC_OBJECT_LOOKS_VALID(cell) do { \ + if (!(cell))\ + CRASH();\ + if (cell->unvalidatedStructure()->unvalidatedStructure() != cell->unvalidatedStructure()->unvalidatedStructure()->unvalidatedStructure())\ + CRASH();\ +} while (0) + +#define ASSERT_GC_OBJECT_INHERITS(object, classInfo) do {\ + ASSERT_GC_OBJECT_LOOKS_VALID(object); \ + if (!object->inherits(classInfo)) \ + CRASH();\ +} while (0) + +#else +#define ASSERT_GC_OBJECT_LOOKS_VALID(cell) do { (void)cell; } while (0) +#define ASSERT_GC_OBJECT_INHERITS(object, classInfo) do { (void)object; (void)classInfo; } while (0) +#endif + +#if COMPILER(CLANG) +#define ASSERT_HAS_TRIVIAL_DESTRUCTOR(klass) COMPILE_ASSERT(__has_trivial_destructor(klass), klass##_has_trivial_destructor_check) +#else +#define ASSERT_HAS_TRIVIAL_DESTRUCTOR(klass) +#endif + +#endif /* WTF_Assertions_h */ diff --git a/Source/JavaScriptCore/wtf/Atomics.h b/Source/JavaScriptCore/wtf/Atomics.h new file mode 100644 index 000000000..5e10460c6 --- /dev/null +++ b/Source/JavaScriptCore/wtf/Atomics.h @@ -0,0 +1,196 @@ +/* + * Copyright (C) 2007, 2008, 2010 Apple Inc. All rights reserved. + * Copyright (C) 2007 Justin Haygood (jhaygood@reaktix.com) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of Apple Computer, Inc. ("Apple") 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 APPLE AND ITS 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 APPLE OR ITS 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. + * + * + * Note: The implementations of InterlockedIncrement and InterlockedDecrement are based + * on atomic_increment and atomic_exchange_and_add from the Boost C++ Library. The license + * is virtually identical to the Apple license above but is included here for completeness. + * + * Boost Software License - Version 1.0 - August 17th, 2003 + * + * Permission is hereby granted, free of charge, to any person or organization + * obtaining a copy of the software and accompanying documentation covered by + * this license (the "Software") to use, reproduce, display, distribute, + * execute, and transmit the Software, and to prepare derivative works of the + * Software, and to permit third-parties to whom the Software is furnished to + * do so, all subject to the following: + * + * The copyright notices in the Software and this entire statement, including + * the above license grant, this restriction and the following disclaimer, + * must be included in all copies of the Software, in whole or in part, and + * all derivative works of the Software, unless such copies or derivative + * works are solely in the form of machine-executable object code generated by + * a source language processor. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT + * SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE + * FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#ifndef Atomics_h +#define Atomics_h + +#include "Platform.h" +#include "StdLibExtras.h" +#include "UnusedParam.h" + +#if OS(WINDOWS) +#include <windows.h> +#elif OS(DARWIN) +#include <libkern/OSAtomic.h> +#elif OS(QNX) +#include <atomic.h> +#elif OS(ANDROID) +#include <sys/atomics.h> +#elif COMPILER(GCC) +#if (__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 2)) +#include <ext/atomicity.h> +#else +#include <bits/atomicity.h> +#endif +#endif + +namespace WTF { + +#if OS(WINDOWS) +#define WTF_USE_LOCKFREE_THREADSAFEREFCOUNTED 1 + +#if COMPILER(MINGW) || COMPILER(MSVC7_OR_LOWER) || OS(WINCE) +inline int atomicIncrement(int* addend) { return InterlockedIncrement(reinterpret_cast<long*>(addend)); } +inline int atomicDecrement(int* addend) { return InterlockedDecrement(reinterpret_cast<long*>(addend)); } +#else +inline int atomicIncrement(int volatile* addend) { return InterlockedIncrement(reinterpret_cast<long volatile*>(addend)); } +inline int atomicDecrement(int volatile* addend) { return InterlockedDecrement(reinterpret_cast<long volatile*>(addend)); } +#endif + +#elif OS(DARWIN) +#define WTF_USE_LOCKFREE_THREADSAFEREFCOUNTED 1 + +inline int atomicIncrement(int volatile* addend) { return OSAtomicIncrement32Barrier(const_cast<int*>(addend)); } +inline int atomicDecrement(int volatile* addend) { return OSAtomicDecrement32Barrier(const_cast<int*>(addend)); } + +#elif OS(QNX) +#define WTF_USE_LOCKFREE_THREADSAFEREFCOUNTED 1 + +// Note, atomic_{add, sub}_value() return the previous value of addend's content. +inline int atomicIncrement(int volatile* addend) { return static_cast<int>(atomic_add_value(reinterpret_cast<unsigned volatile*>(addend), 1)) + 1; } +inline int atomicDecrement(int volatile* addend) { return static_cast<int>(atomic_sub_value(reinterpret_cast<unsigned volatile*>(addend), 1)) - 1; } + +#elif OS(ANDROID) + +inline int atomicIncrement(int volatile* addend) { return __atomic_inc(addend); } +inline int atomicDecrement(int volatile* addend) { return __atomic_dec(addend); } + +#elif COMPILER(GCC) && !CPU(SPARC64) // sizeof(_Atomic_word) != sizeof(int) on sparc64 gcc +#define WTF_USE_LOCKFREE_THREADSAFEREFCOUNTED 1 + +inline int atomicIncrement(int volatile* addend) { return __gnu_cxx::__exchange_and_add(addend, 1) + 1; } +inline int atomicDecrement(int volatile* addend) { return __gnu_cxx::__exchange_and_add(addend, -1) - 1; } + +#endif + +inline bool weakCompareAndSwap(unsigned* location, unsigned expected, unsigned newValue) +{ +#if ENABLE(COMPARE_AND_SWAP) + bool result; +#if CPU(X86) || CPU(X86_64) + asm volatile( + "lock; cmpxchgl %3, %2\n\t" + "sete %1" + : "+a"(expected), "=q"(result), "+m"(*location) + : "r"(newValue) + : "memory" + ); +#elif CPU(ARM_THUMB2) + unsigned tmp; + asm volatile( + "movw %1, #1\n\t" + "ldrex %2, %0\n\t" + "cmp %3, %2\n\t" + "bne.n 0f\n\t" + "strex %1, %4, %0\n\t" + "0:" + : "+m"(*location), "=&r"(result), "=&r"(tmp) + : "r"(expected), "r"(newValue) + : "memory"); + result = !result; +#else +#error "Bad architecture for compare and swap." +#endif + return result; +#else + UNUSED_PARAM(location); + UNUSED_PARAM(expected); + UNUSED_PARAM(newValue); + CRASH(); + return 0; +#endif +} + +inline bool weakCompareAndSwap(void*volatile* location, void* expected, void* newValue) +{ +#if ENABLE(COMPARE_AND_SWAP) +#if CPU(X86_64) + bool result; + asm volatile( + "lock; cmpxchgq %3, %2\n\t" + "sete %1" + : "+a"(expected), "=q"(result), "+m"(*location) + : "r"(newValue) + : "memory" + ); + return result; +#else + return weakCompareAndSwap(bitwise_cast<unsigned*>(location), bitwise_cast<unsigned>(expected), bitwise_cast<unsigned>(newValue)); +#endif +#else // ENABLE(COMPARE_AND_SWAP) + UNUSED_PARAM(location); + UNUSED_PARAM(expected); + UNUSED_PARAM(newValue); + CRASH(); + return 0; +#endif // ENABLE(COMPARE_AND_SWAP) +} + +inline bool weakCompareAndSwap(volatile uintptr_t* location, uintptr_t expected, uintptr_t newValue) +{ + return weakCompareAndSwap(reinterpret_cast<void*volatile*>(location), reinterpret_cast<void*>(expected), reinterpret_cast<void*>(newValue)); +} + +} // namespace WTF + +#if USE(LOCKFREE_THREADSAFEREFCOUNTED) +using WTF::atomicDecrement; +using WTF::atomicIncrement; +#endif + +#endif // Atomics_h diff --git a/Source/JavaScriptCore/wtf/BitVector.cpp b/Source/JavaScriptCore/wtf/BitVector.cpp new file mode 100644 index 000000000..49dc21129 --- /dev/null +++ b/Source/JavaScriptCore/wtf/BitVector.cpp @@ -0,0 +1,120 @@ +/* + * Copyright (C) 2011 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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 APPLE INC. ``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 APPLE INC. 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 "config.h" +#include "BitVector.h" + +#include <algorithm> +#include <string.h> +#include <wtf/Assertions.h> +#include <wtf/FastMalloc.h> +#include <wtf/StdLibExtras.h> + +namespace WTF { + +void BitVector::setSlow(const BitVector& other) +{ + uintptr_t newBitsOrPointer; + if (other.isInline()) + newBitsOrPointer = other.m_bitsOrPointer; + else { + OutOfLineBits* newOutOfLineBits = OutOfLineBits::create(other.size()); + memcpy(newOutOfLineBits->bits(), other.bits(), byteCount(other.size())); + newBitsOrPointer = bitwise_cast<uintptr_t>(newOutOfLineBits) >> 1; + } + if (!isInline()) + OutOfLineBits::destroy(outOfLineBits()); + m_bitsOrPointer = newBitsOrPointer; +} + +void BitVector::resize(size_t numBits) +{ + if (numBits <= maxInlineBits()) { + if (isInline()) + return; + + OutOfLineBits* myOutOfLineBits = outOfLineBits(); + m_bitsOrPointer = makeInlineBits(*myOutOfLineBits->bits()); + OutOfLineBits::destroy(myOutOfLineBits); + return; + } + + resizeOutOfLine(numBits); +} + +void BitVector::clearAll() +{ + if (isInline()) + m_bitsOrPointer = makeInlineBits(0); + else + memset(outOfLineBits()->bits(), 0, byteCount(size())); +} + +BitVector::OutOfLineBits* BitVector::OutOfLineBits::create(size_t numBits) +{ + numBits = (numBits + bitsInPointer() - 1) & ~(bitsInPointer() - 1); + size_t size = sizeof(OutOfLineBits) + sizeof(uintptr_t) * (numBits / bitsInPointer()); + OutOfLineBits* result = new (NotNull, fastMalloc(size)) OutOfLineBits(numBits); + return result; +} + +void BitVector::OutOfLineBits::destroy(OutOfLineBits* outOfLineBits) +{ + fastFree(outOfLineBits); +} + +void BitVector::resizeOutOfLine(size_t numBits) +{ + ASSERT(numBits > maxInlineBits()); + OutOfLineBits* newOutOfLineBits = OutOfLineBits::create(numBits); + if (isInline()) { + // Make sure that all of the bits are zero in case we do a no-op resize. + *newOutOfLineBits->bits() = m_bitsOrPointer & ~(static_cast<uintptr_t>(1) << maxInlineBits()); + } else { + if (numBits > size()) { + size_t oldNumWords = outOfLineBits()->numWords(); + size_t newNumWords = newOutOfLineBits->numWords(); + memcpy(newOutOfLineBits->bits(), outOfLineBits()->bits(), oldNumWords * sizeof(void*)); + memset(newOutOfLineBits->bits() + oldNumWords, 0, (newNumWords - oldNumWords) * sizeof(void*)); + } else + memcpy(newOutOfLineBits->bits(), outOfLineBits()->bits(), newOutOfLineBits->numWords() * sizeof(void*)); + OutOfLineBits::destroy(outOfLineBits()); + } + m_bitsOrPointer = bitwise_cast<uintptr_t>(newOutOfLineBits) >> 1; +} + +#ifndef NDEBUG +void BitVector::dump(FILE* out) +{ + for (size_t i = 0; i < size(); ++i) { + if (get(i)) + fprintf(out, "1"); + else + fprintf(out, "-"); + } +} +#endif + +} // namespace WTF diff --git a/Source/JavaScriptCore/wtf/BitVector.h b/Source/JavaScriptCore/wtf/BitVector.h new file mode 100644 index 000000000..109d3ffcf --- /dev/null +++ b/Source/JavaScriptCore/wtf/BitVector.h @@ -0,0 +1,243 @@ +/* + * Copyright (C) 2011 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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 APPLE INC. ``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 APPLE INC. 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. + */ + +#ifndef BitVector_h +#define BitVector_h + +#include <wtf/Assertions.h> +#include <wtf/StdLibExtras.h> + +#ifndef NDEBUG +#include <stdio.h> +#endif + +namespace WTF { + +// This is a space-efficient, resizeable bitvector class. In the common case it +// occupies one word, but if necessary, it will inflate this one word to point +// to a single chunk of out-of-line allocated storage to store an arbitrary number +// of bits. +// +// - The bitvector remembers the bound of how many bits can be stored, but this +// may be slightly greater (by as much as some platform-specific constant) +// than the last argument passed to ensureSize(). +// +// - The bitvector can resize itself automatically (set, clear, get) or can be used +// in a manual mode, which is faster (quickSet, quickClear, quickGet, ensureSize). +// +// - Accesses ASSERT that you are within bounds. +// +// - Bits are automatically initialized to zero. +// +// On the other hand, this BitVector class may not be the fastest around, since +// it does conditionals on every get/set/clear. But it is great if you need to +// juggle a lot of variable-length BitVectors and you're worried about wasting +// space. + +class BitVector { +public: + BitVector() + : m_bitsOrPointer(makeInlineBits(0)) + { + } + + explicit BitVector(size_t numBits) + : m_bitsOrPointer(makeInlineBits(0)) + { + ensureSize(numBits); + } + + BitVector(const BitVector& other) + : m_bitsOrPointer(makeInlineBits(0)) + { + (*this) = other; + } + + + ~BitVector() + { + if (isInline()) + return; + OutOfLineBits::destroy(outOfLineBits()); + } + + BitVector& operator=(const BitVector& other) + { + if (isInline() && other.isInline()) + m_bitsOrPointer = other.m_bitsOrPointer; + else + setSlow(other); + return *this; + } + + size_t size() const + { + if (isInline()) + return maxInlineBits(); + return outOfLineBits()->numBits(); + } + + void ensureSize(size_t numBits) + { + if (numBits <= size()) + return; + resizeOutOfLine(numBits); + } + + // Like ensureSize(), but supports reducing the size of the bitvector. + void resize(size_t numBits); + + void clearAll(); + + bool quickGet(size_t bit) const + { + ASSERT(bit < size()); + return !!(bits()[bit / bitsInPointer()] & (static_cast<uintptr_t>(1) << (bit & (bitsInPointer() - 1)))); + } + + void quickSet(size_t bit) + { + ASSERT(bit < size()); + bits()[bit / bitsInPointer()] |= (static_cast<uintptr_t>(1) << (bit & (bitsInPointer() - 1))); + } + + void quickClear(size_t bit) + { + ASSERT(bit < size()); + bits()[bit / bitsInPointer()] &= ~(static_cast<uintptr_t>(1) << (bit & (bitsInPointer() - 1))); + } + + void quickSet(size_t bit, bool value) + { + if (value) + quickSet(bit); + else + quickClear(bit); + } + + bool get(size_t bit) const + { + if (bit >= size()) + return false; + return quickGet(bit); + } + + void set(size_t bit) + { + ensureSize(bit + 1); + quickSet(bit); + } + + void clear(size_t bit) + { + if (bit >= size()) + return; + quickClear(bit); + } + + void set(size_t bit, bool value) + { + if (value) + set(bit); + else + clear(bit); + } + +#ifndef NDEBUG + void dump(FILE* out); +#endif + +private: + static unsigned bitsInPointer() + { + return sizeof(void*) << 3; + } + + static unsigned maxInlineBits() + { + return bitsInPointer() - 1; + } + + static size_t byteCount(size_t bitCount) + { + return (bitCount + 7) >> 3; + } + + static uintptr_t makeInlineBits(uintptr_t bits) + { + ASSERT(!(bits & (static_cast<uintptr_t>(1) << maxInlineBits()))); + return bits | (static_cast<uintptr_t>(1) << maxInlineBits()); + } + + class OutOfLineBits { + public: + size_t numBits() const { return m_numBits; } + size_t numWords() const { return (m_numBits + bitsInPointer() - 1) / bitsInPointer(); } + uintptr_t* bits() { return bitwise_cast<uintptr_t*>(this + 1); } + const uintptr_t* bits() const { return bitwise_cast<const uintptr_t*>(this + 1); } + + static OutOfLineBits* create(size_t numBits); + + static void destroy(OutOfLineBits*); + + private: + OutOfLineBits(size_t numBits) + : m_numBits(numBits) + { + } + + size_t m_numBits; + }; + + bool isInline() const { return m_bitsOrPointer >> maxInlineBits(); } + + const OutOfLineBits* outOfLineBits() const { return bitwise_cast<const OutOfLineBits*>(m_bitsOrPointer << 1); } + OutOfLineBits* outOfLineBits() { return bitwise_cast<OutOfLineBits*>(m_bitsOrPointer << 1); } + + void resizeOutOfLine(size_t numBits); + void setSlow(const BitVector& other); + + uintptr_t* bits() + { + if (isInline()) + return &m_bitsOrPointer; + return outOfLineBits()->bits(); + } + + const uintptr_t* bits() const + { + if (isInline()) + return &m_bitsOrPointer; + return outOfLineBits()->bits(); + } + + uintptr_t m_bitsOrPointer; +}; + +} // namespace WTF + +using WTF::BitVector; + +#endif // BitVector_h diff --git a/Source/JavaScriptCore/wtf/Bitmap.h b/Source/JavaScriptCore/wtf/Bitmap.h new file mode 100644 index 000000000..d7e2528a3 --- /dev/null +++ b/Source/JavaScriptCore/wtf/Bitmap.h @@ -0,0 +1,225 @@ +/* + * Copyright (C) 2010 Apple Inc. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ +#ifndef Bitmap_h +#define Bitmap_h + +#include "Atomics.h" +#include "FixedArray.h" +#include "StdLibExtras.h" +#include <stdint.h> +#include <string.h> + +namespace WTF { + +enum BitmapAtomicMode { + // This makes concurrentTestAndSet behave just like testAndSet. + BitmapNotAtomic, + + // This makes concurrentTestAndSet use compareAndSwap, so that it's + // atomic even when used concurrently. + BitmapAtomic +}; + +template<size_t size, BitmapAtomicMode atomicMode = BitmapNotAtomic> +class Bitmap { +private: + typedef uint32_t WordType; + +public: + Bitmap(); + + bool get(size_t) const; + void set(size_t); + bool testAndSet(size_t); + bool testAndClear(size_t); + bool concurrentTestAndSet(size_t); + bool concurrentTestAndClear(size_t); + size_t nextPossiblyUnset(size_t) const; + void clear(size_t); + void clearAll(); + int64_t findRunOfZeros(size_t) const; + size_t count(size_t = 0) const; + size_t isEmpty() const; + size_t isFull() const; + +private: + static const WordType wordSize = sizeof(WordType) * 8; + static const WordType words = (size + wordSize - 1) / wordSize; + + // the literal '1' is of type signed int. We want to use an unsigned + // version of the correct size when doing the calculations because if + // WordType is larger than int, '1 << 31' will first be sign extended + // and then casted to unsigned, meaning that set(31) when WordType is + // a 64 bit unsigned int would give 0xffff8000 + static const WordType one = 1; + + FixedArray<WordType, words> bits; +}; + +template<size_t size, BitmapAtomicMode atomicMode> +inline Bitmap<size, atomicMode>::Bitmap() +{ + clearAll(); +} + +template<size_t size, BitmapAtomicMode atomicMode> +inline bool Bitmap<size, atomicMode>::get(size_t n) const +{ + return !!(bits[n / wordSize] & (one << (n % wordSize))); +} + +template<size_t size, BitmapAtomicMode atomicMode> +inline void Bitmap<size, atomicMode>::set(size_t n) +{ + bits[n / wordSize] |= (one << (n % wordSize)); +} + +template<size_t size, BitmapAtomicMode atomicMode> +inline bool Bitmap<size, atomicMode>::testAndSet(size_t n) +{ + WordType mask = one << (n % wordSize); + size_t index = n / wordSize; + bool result = bits[index] & mask; + bits[index] |= mask; + return result; +} + +template<size_t size, BitmapAtomicMode atomicMode> +inline bool Bitmap<size, atomicMode>::testAndClear(size_t n) +{ + WordType mask = one << (n % wordSize); + size_t index = n / wordSize; + bool result = bits[index] & mask; + bits[index] &= ~mask; + return result; +} + +template<size_t size, BitmapAtomicMode atomicMode> +inline bool Bitmap<size, atomicMode>::concurrentTestAndSet(size_t n) +{ + if (atomicMode == BitmapNotAtomic) + return testAndSet(n); + + ASSERT(atomicMode == BitmapAtomic); + + WordType mask = one << (n % wordSize); + size_t index = n / wordSize; + WordType* wordPtr = bits.data() + index; + WordType oldValue; + do { + oldValue = *wordPtr; + if (oldValue & mask) + return true; + } while (!weakCompareAndSwap(wordPtr, oldValue, oldValue | mask)); + return false; +} + +template<size_t size, BitmapAtomicMode atomicMode> +inline bool Bitmap<size, atomicMode>::concurrentTestAndClear(size_t n) +{ + if (atomicMode == BitmapNotAtomic) + return testAndClear(n); + + ASSERT(atomicMode == BitmapAtomic); + + WordType mask = one << (n % wordSize); + size_t index = n / wordSize; + WordType* wordPtr = bits.data() + index; + WordType oldValue; + do { + oldValue = *wordPtr; + if (!(oldValue & mask)) + return false; + } while (!weakCompareAndSwap(wordPtr, oldValue, oldValue & ~mask)); + return true; +} + +template<size_t size, BitmapAtomicMode atomicMode> +inline void Bitmap<size, atomicMode>::clear(size_t n) +{ + bits[n / wordSize] &= ~(one << (n % wordSize)); +} + +template<size_t size, BitmapAtomicMode atomicMode> +inline void Bitmap<size, atomicMode>::clearAll() +{ + memset(bits.data(), 0, sizeof(bits)); +} + +template<size_t size, BitmapAtomicMode atomicMode> +inline size_t Bitmap<size, atomicMode>::nextPossiblyUnset(size_t start) const +{ + if (!~bits[start / wordSize]) + return ((start / wordSize) + 1) * wordSize; + return start + 1; +} + +template<size_t size, BitmapAtomicMode atomicMode> +inline int64_t Bitmap<size, atomicMode>::findRunOfZeros(size_t runLength) const +{ + if (!runLength) + runLength = 1; + + for (size_t i = 0; i <= (size - runLength) ; i++) { + bool found = true; + for (size_t j = i; j <= (i + runLength - 1) ; j++) { + if (get(j)) { + found = false; + break; + } + } + if (found) + return i; + } + return -1; +} + +template<size_t size, BitmapAtomicMode atomicMode> +inline size_t Bitmap<size, atomicMode>::count(size_t start) const +{ + size_t result = 0; + for ( ; (start % wordSize); ++start) { + if (get(start)) + ++result; + } + for (size_t i = start / wordSize; i < words; ++i) + result += WTF::bitCount(bits[i]); + return result; +} + +template<size_t size, BitmapAtomicMode atomicMode> +inline size_t Bitmap<size, atomicMode>::isEmpty() const +{ + for (size_t i = 0; i < words; ++i) + if (bits[i]) + return false; + return true; +} + +template<size_t size, BitmapAtomicMode atomicMode> +inline size_t Bitmap<size, atomicMode>::isFull() const +{ + for (size_t i = 0; i < words; ++i) + if (~bits[i]) + return false; + return true; +} + +} +#endif diff --git a/Source/JavaScriptCore/wtf/BlockStack.h b/Source/JavaScriptCore/wtf/BlockStack.h new file mode 100644 index 000000000..61e108db3 --- /dev/null +++ b/Source/JavaScriptCore/wtf/BlockStack.h @@ -0,0 +1,96 @@ +/* + * Copyright (C) 2011 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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 APPLE INC. AND ITS 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 APPLE INC. OR ITS 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. + */ + +#ifndef BlockStack_h +#define BlockStack_h + +#include <wtf/Assertions.h> +#include <wtf/FastMalloc.h> +#include <wtf/Vector.h> + +namespace WTF { + +template <typename T> class BlockStack { +public: + static const size_t blockSize = 4096; + static const size_t blockLength = blockSize / sizeof(T); + + BlockStack(); + ~BlockStack(); + + T* grow(); + void shrink(T*); + + const Vector<T*>& blocks(); + +private: + Vector<T*> m_blocks; + T* m_spareBlock; // Used to avoid thrash at block boundaries. +}; + +template <typename T> BlockStack<T>::BlockStack() + : m_spareBlock(0) +{ +} + +template <typename T> BlockStack<T>::~BlockStack() +{ + if (m_spareBlock) + fastFree(m_spareBlock); + for (size_t i = 0; i < m_blocks.size(); ++i) + fastFree(m_blocks[i]); +} + +template <typename T> inline const Vector<T*>& BlockStack<T>::blocks() +{ + return m_blocks; +} + +template <typename T> T* BlockStack<T>::grow() +{ + T* block = m_spareBlock ? m_spareBlock : static_cast<T*>(fastMalloc(blockSize)); + m_spareBlock = 0; + + m_blocks.append(block); + return block; +} + +template <typename T> void BlockStack<T>::shrink(T* newEnd) +{ + ASSERT(newEnd != m_blocks.last() + blockLength); + m_spareBlock = m_blocks.last(); + m_blocks.removeLast(); + + while (m_blocks.last() + blockLength != newEnd) { + fastFree(m_blocks.last()); + m_blocks.removeLast(); + } +} + +} + +using WTF::BlockStack; + +#endif diff --git a/Source/JavaScriptCore/wtf/BloomFilter.h b/Source/JavaScriptCore/wtf/BloomFilter.h new file mode 100644 index 000000000..f81d83e21 --- /dev/null +++ b/Source/JavaScriptCore/wtf/BloomFilter.h @@ -0,0 +1,139 @@ +/* + * Copyright (C) 2011 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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 APPLE INC. AND ITS 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 APPLE INC. OR ITS 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. + */ + +#ifndef BloomFilter_h +#define BloomFilter_h + +#include <wtf/AlwaysInline.h> +#include <wtf/text/AtomicString.h> + +namespace WTF { + +// Counting bloom filter with k=2 and 8 bit counters. Uses 2^keyBits bytes of memory. +// False positive rate is approximately (1-e^(-2n/m))^2, where n is the number of unique +// keys and m is the table size (==2^keyBits). +template <unsigned keyBits> +class BloomFilter { +public: + COMPILE_ASSERT(keyBits <= 16, bloom_filter_key_size); + + static const size_t tableSize = 1 << keyBits; + static const unsigned keyMask = (1 << keyBits) - 1; + static uint8_t maximumCount() { return std::numeric_limits<uint8_t>::max(); } + + BloomFilter() { clear(); } + + void add(unsigned hash); + void remove(unsigned hash); + + // The filter may give false positives (claim it may contain a key it doesn't) + // but never false negatives (claim it doesn't contain a key it does). + bool mayContain(unsigned hash) const { return firstSlot(hash) && secondSlot(hash); } + + // The filter must be cleared before reuse even if all keys are removed. + // Otherwise overflowed keys will stick around. + void clear(); + + void add(const AtomicString& string) { add(string.impl()->existingHash()); } + void add(const String& string) { add(string.impl()->hash()); } + void remove(const AtomicString& string) { remove(string.impl()->existingHash()); } + void remove(const String& string) { remove(string.impl()->hash()); } + + bool mayContain(const AtomicString& string) const { return mayContain(string.impl()->existingHash()); } + bool mayContain(const String& string) const { return mayContain(string.impl()->hash()); } + +#if !ASSERT_DISABLED + // Slow. + bool likelyEmpty() const; + bool isClear() const; +#endif + +private: + uint8_t& firstSlot(unsigned hash) { return m_table[hash & keyMask]; } + uint8_t& secondSlot(unsigned hash) { return m_table[(hash >> 16) & keyMask]; } + const uint8_t& firstSlot(unsigned hash) const { return m_table[hash & keyMask]; } + const uint8_t& secondSlot(unsigned hash) const { return m_table[(hash >> 16) & keyMask]; } + + uint8_t m_table[tableSize]; +}; + +template <unsigned keyBits> +inline void BloomFilter<keyBits>::add(unsigned hash) +{ + uint8_t& first = firstSlot(hash); + uint8_t& second = secondSlot(hash); + if (LIKELY(first < maximumCount())) + ++first; + if (LIKELY(second < maximumCount())) + ++second; +} + +template <unsigned keyBits> +inline void BloomFilter<keyBits>::remove(unsigned hash) +{ + uint8_t& first = firstSlot(hash); + uint8_t& second = secondSlot(hash); + ASSERT(first); + ASSERT(second); + // In case of an overflow, the slot sticks in the table until clear(). + if (LIKELY(first < maximumCount())) + --first; + if (LIKELY(second < maximumCount())) + --second; +} + +template <unsigned keyBits> +inline void BloomFilter<keyBits>::clear() +{ + memset(m_table, 0, tableSize); +} + +#if !ASSERT_DISABLED +template <unsigned keyBits> +bool BloomFilter<keyBits>::likelyEmpty() const +{ + for (size_t n = 0; n < tableSize; ++n) { + if (m_table[n] && m_table[n] != maximumCount()) + return false; + } + return true; +} + +template <unsigned keyBits> +bool BloomFilter<keyBits>::isClear() const +{ + for (size_t n = 0; n < tableSize; ++n) { + if (m_table[n]) + return false; + } + return true; +} +#endif + +} + +using WTF::BloomFilter; + +#endif diff --git a/Source/JavaScriptCore/wtf/BoundsCheckedPointer.h b/Source/JavaScriptCore/wtf/BoundsCheckedPointer.h new file mode 100644 index 000000000..d5d42fc1c --- /dev/null +++ b/Source/JavaScriptCore/wtf/BoundsCheckedPointer.h @@ -0,0 +1,287 @@ +/* + * Copyright (C) 2011 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of Apple Computer, Inc. ("Apple") 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 APPLE AND ITS 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 APPLE OR ITS 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. + */ + +#ifndef WTF_BoundsCheckedPointer_h +#define WTF_BoundsCheckedPointer_h + +#include "Assertions.h" +#include "UnusedParam.h" + +namespace WTF { + +// Useful for when you'd like to do pointer arithmetic on a buffer, but +// you'd also like to get some ASSERT()'s that prevent you from overflowing. +// This should be performance-neutral in release builds, while providing +// you with strong assertions in debug builds. Note that all of the +// asserting happens when you actually access the pointer. You are allowed +// to overflow or underflow with arithmetic so long as no accesses are +// performed. + +template<typename T> +class BoundsCheckedPointer { +public: + BoundsCheckedPointer() + : m_pointer(0) +#if !ASSERT_DISABLED + , m_begin(0) + , m_end(0) +#endif + { + } + + BoundsCheckedPointer(T* pointer, size_t numElements) + : m_pointer(pointer) +#if !ASSERT_DISABLED + , m_begin(pointer) + , m_end(pointer + numElements) +#endif + { + UNUSED_PARAM(numElements); + } + + BoundsCheckedPointer(T* pointer, T* end) + : m_pointer(pointer) +#if !ASSERT_DISABLED + , m_begin(pointer) + , m_end(end) +#endif + { + UNUSED_PARAM(end); + } + + BoundsCheckedPointer(T* pointer, T* begin, size_t numElements) + : m_pointer(pointer) +#if !ASSERT_DISABLED + , m_begin(begin) + , m_end(begin + numElements) +#endif + { + UNUSED_PARAM(begin); + UNUSED_PARAM(numElements); + } + + BoundsCheckedPointer(T* pointer, T* begin, T* end) + : m_pointer(pointer) +#if !ASSERT_DISABLED + , m_begin(begin) + , m_end(end) +#endif + { + UNUSED_PARAM(begin); + UNUSED_PARAM(end); + } + + BoundsCheckedPointer& operator=(T* value) + { + m_pointer = value; + return *this; + } + + BoundsCheckedPointer& operator+=(ptrdiff_t amount) + { + m_pointer += amount; + return *this; + } + + BoundsCheckedPointer& operator-=(ptrdiff_t amount) + { + m_pointer -= amount; + return *this; + } + + BoundsCheckedPointer operator+(ptrdiff_t amount) const + { + BoundsCheckedPointer result = *this; + result.m_pointer += amount; + return result; + } + + BoundsCheckedPointer operator-(ptrdiff_t amount) const + { + BoundsCheckedPointer result = *this; + result.m_pointer -= amount; + return result; + } + + BoundsCheckedPointer operator++() // prefix + { + m_pointer++; + return *this; + } + + BoundsCheckedPointer operator--() // prefix + { + m_pointer--; + return *this; + } + + BoundsCheckedPointer operator++(int) // postfix + { + BoundsCheckedPointer result = *this; + m_pointer++; + return result; + } + + BoundsCheckedPointer operator--(int) // postfix + { + BoundsCheckedPointer result = *this; + m_pointer--; + return result; + } + + bool operator<(T* other) const + { + return m_pointer < other; + } + + bool operator<=(T* other) const + { + return m_pointer <= other; + } + + bool operator>(T* other) const + { + return m_pointer > other; + } + + bool operator>=(T* other) const + { + return m_pointer >= other; + } + + bool operator==(T* other) const + { + return m_pointer == other; + } + + bool operator!=(T* other) const + { + return m_pointer != other; + } + + bool operator<(BoundsCheckedPointer other) const + { + return m_pointer < other.m_pointer; + } + + bool operator<=(BoundsCheckedPointer other) const + { + return m_pointer <= other.m_pointer; + } + + bool operator>(BoundsCheckedPointer other) const + { + return m_pointer > other.m_pointer; + } + + bool operator>=(BoundsCheckedPointer other) const + { + return m_pointer >= other.m_pointer; + } + + bool operator==(BoundsCheckedPointer other) const + { + return m_pointer == other.m_pointer; + } + + bool operator!=(BoundsCheckedPointer other) const + { + return m_pointer != other.m_pointer; + } + + BoundsCheckedPointer operator!() + { + return !m_pointer; + } + + T* get() + { + return m_pointer; + } + + T& operator*() + { + validate(); + return *m_pointer; + } + + const T& operator*() const + { + validate(); + return *m_pointer; + } + + T& operator[](ptrdiff_t index) + { + validate(m_pointer + index); + return m_pointer[index]; + } + + const T& operator[](ptrdiff_t index) const + { + validate(m_pointer + index); + return m_pointer[index]; + } + + // The only thing this has in common with strcat() is that it + // keeps appending from the given pointer until reaching 0. + BoundsCheckedPointer& strcat(const T* source) + { + while (*source) + *(*this)++ = *source++; + return *this; + } + +private: + void validate(T* pointer) const + { + ASSERT_UNUSED(pointer, pointer >= m_begin); + + // This guard is designed to protect against the misaligned case. + // A simple pointer < m_end would miss the case if, for example, + // T = int16_t and pointer is 1 byte less than m_end. + ASSERT_UNUSED(pointer, pointer + 1 <= m_end); + } + + void validate() const + { + validate(m_pointer); + } + + T* m_pointer; +#if !ASSERT_DISABLED + T* m_begin; + T* m_end; +#endif +}; + +} // namespace WTF + +using WTF::BoundsCheckedPointer; + +#endif // WTF_BoundsCheckedPointer_h diff --git a/Source/JavaScriptCore/wtf/BumpPointerAllocator.h b/Source/JavaScriptCore/wtf/BumpPointerAllocator.h new file mode 100644 index 000000000..12911a4b0 --- /dev/null +++ b/Source/JavaScriptCore/wtf/BumpPointerAllocator.h @@ -0,0 +1,250 @@ +/* + * Copyright (C) 2010 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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 APPLE INC. ``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 APPLE INC. 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. + */ + +#ifndef BumpPointerAllocator_h +#define BumpPointerAllocator_h + +#include <wtf/PageAllocation.h> + +namespace WTF { + +#define MINIMUM_BUMP_POOL_SIZE 0x1000 + +class BumpPointerPool { +public: + // ensureCapacity will check whether the current pool has capacity to + // allocate 'size' bytes of memory If it does not, it will attempt to + // allocate a new pool (which will be added to this one in a chain). + // + // If allocation fails (out of memory) this method will return null. + // If the return value is non-null, then callers should update any + // references they have to this current (possibly full) BumpPointerPool + // to instead point to the newly returned BumpPointerPool. + BumpPointerPool* ensureCapacity(size_t size) + { + void* allocationEnd = static_cast<char*>(m_current) + size; + ASSERT(allocationEnd > m_current); // check for overflow + if (allocationEnd <= static_cast<void*>(this)) + return this; + return ensureCapacityCrossPool(this, size); + } + + // alloc should only be called after calling ensureCapacity; as such + // alloc will never fail. + void* alloc(size_t size) + { + void* current = m_current; + void* allocationEnd = static_cast<char*>(current) + size; + ASSERT(allocationEnd > current); // check for overflow + ASSERT(allocationEnd <= static_cast<void*>(this)); + m_current = allocationEnd; + return current; + } + + // The dealloc method releases memory allocated using alloc. Memory + // must be released in a LIFO fashion, e.g. if the client calls alloc + // four times, returning pointer A, B, C, D, then the only valid order + // in which these may be deallocaed is D, C, B, A. + // + // The client may optionally skip some deallocations. In the example + // above, it would be valid to only explicitly dealloc C, A (D being + // dealloced along with C, B along with A). + // + // If pointer was not allocated from this pool (or pools) then dealloc + // will CRASH(). Callers should update any references they have to + // this current BumpPointerPool to instead point to the returned + // BumpPointerPool. + BumpPointerPool* dealloc(void* position) + { + if ((position >= m_start) && (position <= static_cast<void*>(this))) { + ASSERT(position <= m_current); + m_current = position; + return this; + } + return deallocCrossPool(this, position); + } + +private: + // Placement operator new, returns the last 'size' bytes of allocation for use as this. + void* operator new(size_t size, const PageAllocation& allocation) + { + ASSERT(size < allocation.size()); + return reinterpret_cast<char*>(reinterpret_cast<intptr_t>(allocation.base()) + allocation.size()) - size; + } + + BumpPointerPool(const PageAllocation& allocation) + : m_current(allocation.base()) + , m_start(allocation.base()) + , m_next(0) + , m_previous(0) + , m_allocation(allocation) + { + } + + static BumpPointerPool* create(size_t minimumCapacity = 0) + { + // Add size of BumpPointerPool object, check for overflow. + minimumCapacity += sizeof(BumpPointerPool); + if (minimumCapacity < sizeof(BumpPointerPool)) + return 0; + + size_t poolSize = MINIMUM_BUMP_POOL_SIZE; + while (poolSize < minimumCapacity) { + poolSize <<= 1; + // The following if check relies on MINIMUM_BUMP_POOL_SIZE being a power of 2! + ASSERT(!(MINIMUM_BUMP_POOL_SIZE & (MINIMUM_BUMP_POOL_SIZE - 1))); + if (!poolSize) + return 0; + } + + PageAllocation allocation = PageAllocation::allocate(poolSize); + if (!!allocation) + return new (allocation) BumpPointerPool(allocation); + return 0; + } + + void shrink() + { + ASSERT(!m_previous); + m_current = m_start; + while (m_next) { + BumpPointerPool* nextNext = m_next->m_next; + m_next->destroy(); + m_next = nextNext; + } + } + + void destroy() + { + m_allocation.deallocate(); + } + + static BumpPointerPool* ensureCapacityCrossPool(BumpPointerPool* previousPool, size_t size) + { + // The pool passed should not have capacity, so we'll start with the next one. + ASSERT(previousPool); + ASSERT((static_cast<char*>(previousPool->m_current) + size) > previousPool->m_current); // check for overflow + ASSERT((static_cast<char*>(previousPool->m_current) + size) > static_cast<void*>(previousPool)); + BumpPointerPool* pool = previousPool->m_next; + + while (true) { + if (!pool) { + // We've run to the end; allocate a new pool. + pool = BumpPointerPool::create(size); + previousPool->m_next = pool; + pool->m_previous = previousPool; + return pool; + } + + // + void* current = pool->m_current; + void* allocationEnd = static_cast<char*>(current) + size; + ASSERT(allocationEnd > current); // check for overflow + if (allocationEnd <= static_cast<void*>(pool)) + return pool; + } + } + + static BumpPointerPool* deallocCrossPool(BumpPointerPool* pool, void* position) + { + // Should only be called if position is not in the current pool. + ASSERT((position < pool->m_start) || (position > static_cast<void*>(pool))); + + while (true) { + // Unwind the current pool to the start, move back in the chain to the previous pool. + pool->m_current = pool->m_start; + pool = pool->m_previous; + + // position was nowhere in the chain! + if (!pool) + CRASH(); + + if ((position >= pool->m_start) && (position <= static_cast<void*>(pool))) { + ASSERT(position <= pool->m_current); + pool->m_current = position; + return pool; + } + } + } + + void* m_current; + void* m_start; + BumpPointerPool* m_next; + BumpPointerPool* m_previous; + PageAllocation m_allocation; + + friend class BumpPointerAllocator; +}; + +// A BumpPointerAllocator manages a set of BumpPointerPool objects, which +// can be used for LIFO (stack like) allocation. +// +// To begin allocating using this class call startAllocator(). The result +// of this method will be null if the initial pool allocation fails, or a +// pointer to a BumpPointerPool object that can be used to perform +// allocations. Whilst running no memory will be released until +// stopAllocator() is called. At this point all allocations made through +// this allocator will be reaped, and underlying memory may be freed. +// +// (In practice we will still hold on to the initial pool to allow allocation +// to be quickly restared, but aditional pools will be freed). +// +// This allocator is non-renetrant, it is encumbant on the clients to ensure +// startAllocator() is not called again until stopAllocator() has been called. +class BumpPointerAllocator { +public: + BumpPointerAllocator() + : m_head(0) + { + } + + ~BumpPointerAllocator() + { + if (m_head) + m_head->destroy(); + } + + BumpPointerPool* startAllocator() + { + if (!m_head) + m_head = BumpPointerPool::create(); + return m_head; + } + + void stopAllocator() + { + if (m_head) + m_head->shrink(); + } + +private: + BumpPointerPool* m_head; +}; + +} + +using WTF::BumpPointerAllocator; + +#endif // BumpPointerAllocator_h diff --git a/Source/JavaScriptCore/wtf/ByteArray.cpp b/Source/JavaScriptCore/wtf/ByteArray.cpp new file mode 100644 index 000000000..80c603f30 --- /dev/null +++ b/Source/JavaScriptCore/wtf/ByteArray.cpp @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2009 Apple Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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 APPLE INC. ``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 APPLE INC. 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 "config.h" +#include "ByteArray.h" + +#include "StdLibExtras.h" + +namespace WTF { + +PassRefPtr<ByteArray> ByteArray::create(size_t size) +{ + unsigned char* buffer = new unsigned char[size + OBJECT_OFFSETOF(ByteArray, m_data)]; + ASSERT((reinterpret_cast<size_t>(buffer) & 3) == 0); + return adoptRef(new (NotNull, buffer) ByteArray(size)); +} + +} diff --git a/Source/JavaScriptCore/wtf/ByteArray.h b/Source/JavaScriptCore/wtf/ByteArray.h new file mode 100644 index 000000000..009ec5850 --- /dev/null +++ b/Source/JavaScriptCore/wtf/ByteArray.h @@ -0,0 +1,110 @@ +/* + * Copyright (C) 2009 Apple Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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 APPLE INC. ``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 APPLE INC. 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. + */ + +#ifndef ByteArray_h +#define ByteArray_h + +#include <limits.h> +#include <wtf/PassRefPtr.h> +#include <wtf/Platform.h> +#include <wtf/RefCounted.h> +#include <wtf/StdLibExtras.h> + +namespace WTF { + class ByteArray : public RefCountedBase { + public: + unsigned length() const { return m_size; } + + void set(unsigned index, double value) + { + if (index >= m_size) + return; + if (!(value > 0)) // Clamp NaN to 0 + value = 0; + else if (value > 255) + value = 255; + m_data[index] = static_cast<unsigned char>(value + 0.5); + } + + void set(unsigned index, unsigned char value) + { + if (index >= m_size) + return; + m_data[index] = value; + } + + bool get(unsigned index, unsigned char& result) const + { + if (index >= m_size) + return false; + result = m_data[index]; + return true; + } + + unsigned char get(unsigned index) const + { + ASSERT(index < m_size); + return m_data[index]; + } + + unsigned char* data() { return m_data; } + + void clear() { memset(m_data, 0, m_size); } + + void deref() + { + if (derefBase()) { + // We allocated with new unsigned char[] in create(), + // and then used placement new to construct the object. + this->~ByteArray(); + delete[] reinterpret_cast<unsigned char*>(this); + } + } + + static PassRefPtr<ByteArray> create(size_t size); + + static size_t offsetOfSize() { return OBJECT_OFFSETOF(ByteArray, m_size); } + static size_t offsetOfData() { return OBJECT_OFFSETOF(ByteArray, m_data); } + + private: + ByteArray(size_t size) + : m_size(size) + { + } + size_t m_size; +// MSVC can't handle correctly unsized array. +// warning C4200: nonstandard extension used : zero-sized array in struct/union +// Cannot generate copy-ctor or copy-assignment operator when UDT contains a zero-sized array +#if COMPILER(MSVC) && !COMPILER(INTEL) + unsigned char m_data[INT_MAX]; +#else + unsigned char m_data[]; +#endif + }; +} // namespace WTF + +using WTF::ByteArray; + +#endif diff --git a/Source/JavaScriptCore/wtf/CMakeLists.txt b/Source/JavaScriptCore/wtf/CMakeLists.txt new file mode 100644 index 000000000..6d1cee899 --- /dev/null +++ b/Source/JavaScriptCore/wtf/CMakeLists.txt @@ -0,0 +1,226 @@ +SET(WTF_HEADERS + ASCIICType.h + AVLTree.h + Alignment.h + AlwaysInline.h + ArrayBuffer.cpp + ArrayBufferView.cpp + Assertions.h + Atomics.h + BitVector.h + Bitmap.h + BoundsCheckedPointer.h + BumpPointerAllocator.h + ByteArray.h + Compiler.h + Complex.h + CryptographicallyRandomNumber.h + CurrentTime.h + DateMath.h + DecimalNumber.h + Decoder.h + Deque.h + DisallowCType.h + DoublyLinkedList.h + DynamicAnnotations.h + Encoder.h + FastAllocBase.h + FastMalloc.h + FixedArray.h + Forward.h + GetPtr.h + HashCountedSet.h + HashFunctions.h + HashIterators.h + HashMap.h + HashSet.h + HashTable.h + HashTraits.h + HexNumber.h + ListHashSet.h + ListRefPtr.h + Locker.h + MD5.h + MainThread.h + MallocZoneSupport.h + MathExtras.h + MessageQueue.h + MetaAllocator.cpp + MetaAllocator.h + MetaAllocatorHandle.h + NonCopyingSort.h + ThreadRestrictionVerifier.h + Noncopyable.h + NotFound.h + NullPtr.h + OSAllocator.h + OSRandomSource.h + OwnArrayPtr.h + OwnFastMallocPtr.h + OwnPtr.h + OwnPtrCommon.h + PageAllocation.h + PageAllocationAligned.h + PageBlock.h + PageReservation.h + PassOwnArrayPtr.h + PassOwnPtr.h + PassRefPtr.h + PassTraits.h + ParallelJobs.h + ParallelJobsGeneric.h + ParallelJobsLibdispatch.h + ParallelJobsOpenMP.h + Platform.h + PossiblyNull.h + RandomNumber.h + RandomNumberSeed.h + RedBlackTree.h + RefCounted.h + RefCountedLeakCounter.h + RefPtr.h + RefPtrHashMap.h + RetainPtr.h + SegmentedVector.h + SHA1.h + StackBounds.h + StaticConstructors.h + StdLibExtras.h + StringExtras.h + StringHasher.h + TCPackedCache.h + TCPageMap.h + TCSpinLock.h + TCSystemAlloc.h + ThreadIdentifierDataPthreads.h + ThreadSafeRefCounted.h + ThreadSpecific.h + Threading.h + ThreadingPrimitives.h + TypeTraits.h + UnusedParam.h + VMTags.h + ValueCheck.h + Vector.h + VectorTraits.h + WTFThreadData.h + dtoa.h + + dtoa/bignum-dtoa.h + dtoa/bignum.h + dtoa/cached-powers.h + dtoa/diy-fp.h + dtoa/double-conversion.h + dtoa/double.h + dtoa/fast-dtoa.h + dtoa/fixed-dtoa.h + dtoa/strtod.h + dtoa/utils.h + + text/AtomicString.h + text/AtomicStringImpl.h + text/CString.h + text/StringBuffer.h + text/StringHash.h + text/StringImpl.h + text/WTFString.h + + threads/BinarySemaphore.h + + unicode/CharacterNames.h + unicode/Collator.h + unicode/UTF8.h + unicode/Unicode.h +) + +SET(WTF_SOURCES + Assertions.cpp + BitVector.cpp + ByteArray.cpp + CryptographicallyRandomNumber.cpp + CurrentTime.cpp + DateMath.cpp + DecimalNumber.cpp + DynamicAnnotations.cpp + FastMalloc.cpp + HashTable.cpp + MainThread.cpp + MD5.cpp + OSRandomSource.cpp + PageAllocationAligned.cpp + PageBlock.cpp + ParallelJobsGeneric.cpp + RandomNumber.cpp + RefCountedLeakCounter.cpp + SHA1.cpp + StackBounds.cpp + StringExtras.cpp + Threading.cpp + TypeTraits.cpp + WTFThreadData.cpp + dtoa.cpp + + dtoa/bignum-dtoa.cc + dtoa/bignum.cc + dtoa/cached-powers.cc + dtoa/diy-fp.cc + dtoa/double-conversion.cc + dtoa/fast-dtoa.cc + dtoa/fixed-dtoa.cc + dtoa/strtod.cc + + text/AtomicString.cpp + text/CString.cpp + text/StringBuilder.cpp + text/StringImpl.cpp + text/StringStatics.cpp + text/WTFString.cpp + + threads/BinarySemaphore.cpp + + unicode/UTF8.cpp +) + +SET(WTF_INCLUDE_DIRECTORIES + "${JAVASCRIPTCORE_DIR}" + "${JAVASCRIPTCORE_DIR}/wtf" + "${JAVASCRIPTCORE_DIR}/wtf/unicode" + "${JAVASCRIPTCORE_DIR}/wtf/dtoa" + "${JavaScriptCore_INCLUDE_DIRECTORIES}" +) + +SET(WTF_LIBRARIES +) + + +IF (ENABLE_FAST_MALLOC) + LIST(APPEND WTF_SOURCES + TCSystemAlloc.cpp + ) +ELSE () + ADD_DEFINITIONS(-DUSE_SYSTEM_MALLOC=1) +ENDIF() + + +SET(WTF_PORT_FLAGS ) +INCLUDE_IF_EXISTS(${JAVASCRIPTCORE_DIR}/wtf/Platform${PORT}.cmake) + +LIST(APPEND WTF_INCLUDE_DIRECTORIES + "${CMAKE_BINARY_DIR}" + "${CMAKE_SOURCE_DIR}/Source/ThirdParty" +) + +WEBKIT_WRAP_SOURCELIST(${WTF_SOURCES}) +INCLUDE_DIRECTORIES(${WTF_INCLUDE_DIRECTORIES}) +ADD_DEFINITIONS(-DBUILDING_WTF) +ADD_LIBRARY(${WTF_LIBRARY_NAME} ${WTF_LIBRARY_TYPE} ${WTF_HEADERS} ${WTF_SOURCES}) +TARGET_LINK_LIBRARIES(${WTF_LIBRARY_NAME} ${WTF_LIBRARIES}) + +IF (WTF_LINK_FLAGS) + ADD_TARGET_PROPERTIES(${WTF_LIBRARY_NAME} LINK_FLAGS "${WTF_LINK_FLAGS}") +ENDIF () + +IF (SHARED_CORE) + SET_TARGET_PROPERTIES(${WTF_LIBRARY_NAME} PROPERTIES VERSION ${PROJECT_VERSION} SOVERSION ${PROJECT_VERSION_MAJOR}) + INSTALL(TARGETS ${WTF_LIBRARY_NAME} DESTINATION lib) +ENDIF () diff --git a/Source/JavaScriptCore/wtf/CONTRIBUTORS.pthreads-win32 b/Source/JavaScriptCore/wtf/CONTRIBUTORS.pthreads-win32 new file mode 100644 index 000000000..7de0f2606 --- /dev/null +++ b/Source/JavaScriptCore/wtf/CONTRIBUTORS.pthreads-win32 @@ -0,0 +1,137 @@ +This is a copy of CONTRIBUTORS file for the Pthreads-win32 library, downloaded +from http://sourceware.org/cgi-bin/cvsweb.cgi/~checkout~/pthreads/CONTRIBUTORS?rev=1.32&cvsroot=pthreads-win32 + +Included here to compliment the Pthreads-win32 license header in wtf/ThreadingWin.cpp file. +WebKit is using derived sources of ThreadCondition code from Pthreads-win32. + +------------------------------------------------------------------------------- + +Contributors (in approximate order of appearance) + +[See also the ChangeLog file where individuals are +attributed in log entries. Likewise in the FAQ file.] + +Ben Elliston bje at cygnus dot com + Initiated the project; + setup the project infrastructure (CVS, web page, etc.); + early prototype routines. +Ross Johnson rpj at callisto dot canberra dot edu dot au + early prototype routines; + ongoing project coordination/maintenance; + implementation of spin locks and barriers; + various enhancements; + bug fixes; + documentation; + testsuite. +Robert Colquhoun rjc at trump dot net dot au + Early bug fixes. +John E. Bossom John dot Bossom at cognos dot com + Contributed substantial original working implementation; + bug fixes; + ongoing guidance and standards interpretation. +Anders Norlander anorland at hem2 dot passagen dot se + Early enhancements and runtime checking for supported + Win32 routines. +Tor Lillqvist tml at iki dot fi + General enhancements; + early bug fixes to condition variables. +Scott Lightner scott at curriculum dot com + Bug fix. +Kevin Ruland Kevin dot Ruland at anheuser-busch dot com + Various bug fixes. +Mike Russo miker at eai dot com + Bug fix. +Mark E. Armstrong avail at pacbell dot net + Bug fixes. +Lorin Hochstein lmh at xiphos dot ca + general bug fixes; bug fixes to condition variables. +Peter Slacik Peter dot Slacik at tatramed dot sk + Bug fixes. +Mumit Khan khan at xraylith dot wisc dot edu + Fixes to work with Mingw32. +Milan Gardian mg at tatramed dot sk + Bug fixes and reports/analyses of obscure problems. +Aurelio Medina aureliom at crt dot com + First implementation of read-write locks. +Graham Dumpleton Graham dot Dumpleton at ra dot pad dot otc dot telstra dot com dot au + Bug fix in condition variables. +Tristan Savatier tristan at mpegtv dot com + WinCE port. +Erik Hensema erik at hensema dot xs4all dot nl + Bug fixes. +Rich Peters rpeters at micro-magic dot com +Todd Owen towen at lucidcalm dot dropbear dot id dot au + Bug fixes to dll loading. +Jason Nye jnye at nbnet dot nb dot ca + Implementation of async cancelation. +Fred Forester fforest at eticomm dot net +Kevin D. Clark kclark at cabletron dot com +David Baggett dmb at itasoftware dot com + Bug fixes. +Paul Redondo paul at matchvision dot com +Scott McCaskill scott at 3dfx dot com + Bug fixes. +Jef Gearhart jgearhart at tpssys dot com + Bug fix. +Arthur Kantor akantor at bexusa dot com + Mutex enhancements. +Steven Reddie smr at essemer dot com dot au + Bug fix. +Alexander Terekhov TEREKHOV at de dot ibm dot com + Re-implemented and improved read-write locks; + (with Louis Thomas) re-implemented and improved + condition variables; + enhancements to semaphores; + enhancements to mutexes; + new mutex implementation in 'futex' style; + suggested a robust implementation of pthread_once + similar to that implemented by V.Kliathcko; + system clock change handling re CV timeouts; + bug fixes. +Thomas Pfaff tpfaff at gmx dot net + Changes to make C version usable with C++ applications; + re-implemented mutex routines to avoid Win32 mutexes + and TryEnterCriticalSection; + procedure to fix Mingw32 thread-safety issues. +Franco Bez franco dot bez at gmx dot de + procedure to fix Mingw32 thread-safety issues. +Louis Thomas lthomas at arbitrade dot com + (with Alexander Terekhov) re-implemented and improved + condition variables. +David Korn dgk at research dot att dot com + Ported to UWIN. +Phil Frisbie, Jr. phil at hawksoft dot com + Bug fix. +Ralf Brese Ralf dot Brese at pdb4 dot siemens dot de + Bug fix. +prionx at juno dot com prionx at juno dot com + Bug fixes. +Max Woodbury mtew at cds dot duke dot edu + POSIX versioning conditionals; + reduced namespace pollution; + idea to separate routines to reduce statically + linked image sizes. +Rob Fanner rfanner at stonethree dot com + Bug fix. +Michael Johnson michaelj at maine dot rr dot com + Bug fix. +Nicolas Barry boozai at yahoo dot com + Bug fixes. +Piet van Bruggen pietvb at newbridges dot nl + Bug fix. +Makoto Kato raven at oldskool dot jp + AMD64 port. +Panagiotis E. Hadjidoukas peh at hpclab dot ceid dot upatras dot gr + Contributed the QueueUserAPCEx package which + makes preemptive async cancelation possible. +Will Bryant will dot bryant at ecosm dot com + Borland compiler patch and makefile. +Anuj Goyal anuj dot goyal at gmail dot com + Port to Digital Mars compiler. +Gottlob Frege gottlobfrege at gmail dot com + re-implemented pthread_once (version 2) + (pthread_once cancellation added by rpj). +Vladimir Kliatchko vladimir at kliatchko dot com + reimplemented pthread_once with the same form + as described by A.Terekhov (later version 2); + implementation of MCS (Mellor-Crummey/Scott) locks.
\ No newline at end of file diff --git a/Source/JavaScriptCore/wtf/CheckedArithmetic.h b/Source/JavaScriptCore/wtf/CheckedArithmetic.h new file mode 100644 index 000000000..f90efae41 --- /dev/null +++ b/Source/JavaScriptCore/wtf/CheckedArithmetic.h @@ -0,0 +1,693 @@ +/* + * Copyright (C) 2011 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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 APPLE INC. ``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 APPLE INC. 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. + */ + +#ifndef CheckedArithmetic_h +#define CheckedArithmetic_h + +#include "Assertions.h" +#include "TypeTraits.h" + +#include <limits> +#include <stdint.h> + +/* Checked<T> + * + * This class provides a mechanism to perform overflow-safe integer arithmetic + * without having to manually ensure that you have all the required bounds checks + * directly in your code. + * + * There are two modes of operation: + * - The default is Checked<T, CrashOnOverflow>, and crashes at the point + * and overflow has occurred. + * - The alternative is Checked<T, RecordOverflow>, which uses an additional + * byte of storage to track whether an overflow has occurred, subsequent + * unchecked operations will crash if an overflow has occured + * + * It is possible to provide a custom overflow handler, in which case you need + * to support these functions: + * - void overflowed(); + * This function is called when an operation has produced an overflow. + * - bool hasOverflowed(); + * This function must return true if overflowed() has been called on an + * instance and false if it has not. + * - void clearOverflow(); + * Used to reset overflow tracking when a value is being overwritten with + * a new value. + * + * Checked<T> works for all integer types, with the following caveats: + * - Mixing signedness of operands is only supported for types narrower than + * 64bits. + * - It does have a performance impact, so tight loops may want to be careful + * when using it. + * + */ + +namespace WTF { + +class CrashOnOverflow { +protected: + NO_RETURN_DUE_TO_CRASH void overflowed() + { + CRASH(); + } + + void clearOverflow() { } + +public: + bool hasOverflowed() const { return false; } +}; + +class RecordOverflow { +protected: + RecordOverflow() + : m_overflowed(false) + { + } + + void overflowed() + { + m_overflowed = true; + } + + void clearOverflow() + { + m_overflowed = false; + } + +public: + bool hasOverflowed() const { return m_overflowed; } + +private: + unsigned char m_overflowed; +}; + +template <typename T, class OverflowHandler = CrashOnOverflow> class Checked; +template <typename T> struct RemoveChecked; +template <typename T> struct RemoveChecked<Checked<T> >; + +template <typename Target, typename Source, bool targetSigned = std::numeric_limits<Target>::is_signed, bool sourceSigned = std::numeric_limits<Source>::is_signed> struct BoundsChecker; +template <typename Target, typename Source> struct BoundsChecker<Target, Source, false, false> { + static bool inBounds(Source value) + { + // Same signedness so implicit type conversion will always increase precision + // to widest type + return value <= std::numeric_limits<Target>::max(); + } +}; + +template <typename Target, typename Source> struct BoundsChecker<Target, Source, true, true> { + static bool inBounds(Source value) + { + // Same signedness so implicit type conversion will always increase precision + // to widest type + return std::numeric_limits<Target>::min() <= value && value <= std::numeric_limits<Target>::max(); + } +}; + +template <typename Target, typename Source> struct BoundsChecker<Target, Source, false, true> { + static bool inBounds(Source value) + { + // Target is unsigned so any value less than zero is clearly unsafe + if (value < 0) + return false; + // If our (unsigned) Target is the same or greater width we can + // convert value to type Target without losing precision + if (sizeof(Target) >= sizeof(Source)) + return static_cast<Target>(value) <= std::numeric_limits<Target>::max(); + // The signed Source type has greater precision than the target so + // max(Target) -> Source will widen. + return value <= static_cast<Source>(std::numeric_limits<Target>::max()); + } +}; + +template <typename Target, typename Source> struct BoundsChecker<Target, Source, true, false> { + static bool inBounds(Source value) + { + // Signed target with an unsigned source + if (sizeof(Target) <= sizeof(Source)) + return value <= static_cast<Source>(std::numeric_limits<Target>::max()); + // Target is Wider than Source so we're guaranteed to fit any value in + // unsigned Source + return true; + } +}; + +template <typename Target, typename Source, bool SameType = IsSameType<Target, Source>::value> struct BoundsCheckElider; +template <typename Target, typename Source> struct BoundsCheckElider<Target, Source, true> { + static bool inBounds(Source) { return true; } +}; +template <typename Target, typename Source> struct BoundsCheckElider<Target, Source, false> : public BoundsChecker<Target, Source> { +}; + +template <typename Target, typename Source> static inline bool isInBounds(Source value) +{ + return BoundsCheckElider<Target, Source>::inBounds(value); +} + +template <typename T> struct RemoveChecked { + typedef T CleanType; + static const CleanType DefaultValue = 0; +}; + +template <typename T> struct RemoveChecked<Checked<T, CrashOnOverflow> > { + typedef typename RemoveChecked<T>::CleanType CleanType; + static const CleanType DefaultValue = 0; +}; + +template <typename T> struct RemoveChecked<Checked<T, RecordOverflow> > { + typedef typename RemoveChecked<T>::CleanType CleanType; + static const CleanType DefaultValue = 0; +}; + +// The ResultBase and SignednessSelector are used to workaround typeof not being +// available in MSVC +template <typename U, typename V, bool uIsBigger = (sizeof(U) > sizeof(V)), bool sameSize = (sizeof(U) == sizeof(V))> struct ResultBase; +template <typename U, typename V> struct ResultBase<U, V, true, false> { + typedef U ResultType; +}; + +template <typename U, typename V> struct ResultBase<U, V, false, false> { + typedef V ResultType; +}; + +template <typename U> struct ResultBase<U, U, false, true> { + typedef U ResultType; +}; + +template <typename U, typename V, bool uIsSigned = std::numeric_limits<U>::is_signed, bool vIsSigned = std::numeric_limits<V>::is_signed> struct SignednessSelector; +template <typename U, typename V> struct SignednessSelector<U, V, true, true> { + typedef U ResultType; +}; + +template <typename U, typename V> struct SignednessSelector<U, V, false, false> { + typedef U ResultType; +}; + +template <typename U, typename V> struct SignednessSelector<U, V, true, false> { + typedef V ResultType; +}; + +template <typename U, typename V> struct SignednessSelector<U, V, false, true> { + typedef U ResultType; +}; + +template <typename U, typename V> struct ResultBase<U, V, false, true> { + typedef typename SignednessSelector<U, V>::ResultType ResultType; +}; + +template <typename U, typename V> struct Result : ResultBase<typename RemoveChecked<U>::CleanType, typename RemoveChecked<V>::CleanType> { +}; + +template <typename LHS, typename RHS, typename ResultType = typename Result<LHS, RHS>::ResultType, + bool lhsSigned = std::numeric_limits<LHS>::is_signed, bool rhsSigned = std::numeric_limits<RHS>::is_signed> struct ArithmeticOperations; + +template <typename LHS, typename RHS, typename ResultType> struct ArithmeticOperations<LHS, RHS, ResultType, true, true> { + // LHS and RHS are signed types + + // Helper function + static inline bool signsMatch(LHS lhs, RHS rhs) + { + return (lhs ^ rhs) >= 0; + } + + static inline bool add(LHS lhs, RHS rhs, ResultType& result) WARN_UNUSED_RETURN + { + if (signsMatch(lhs, rhs)) { + if (lhs >= 0) { + if ((std::numeric_limits<ResultType>::max() - rhs) < lhs) + return false; + } else { + ResultType temp = lhs - std::numeric_limits<ResultType>::min(); + if (rhs < -temp) + return false; + } + } // if the signs do not match this operation can't overflow + result = lhs + rhs; + return true; + } + + static inline bool sub(LHS lhs, RHS rhs, ResultType& result) WARN_UNUSED_RETURN + { + if (!signsMatch(lhs, rhs)) { + if (lhs >= 0) { + if (lhs > std::numeric_limits<ResultType>::max() + rhs) + return false; + } else { + if (rhs > std::numeric_limits<ResultType>::max() + lhs) + return false; + } + } // if the signs match this operation can't overflow + result = lhs - rhs; + return true; + } + + static inline bool multiply(LHS lhs, RHS rhs, ResultType& result) WARN_UNUSED_RETURN + { + if (signsMatch(lhs, rhs)) { + if (lhs >= 0) { + if (lhs && (std::numeric_limits<ResultType>::max() / lhs) < rhs) + return false; + } else { + if (lhs == std::numeric_limits<ResultType>::min() || rhs == std::numeric_limits<ResultType>::min()) + return false; + if ((std::numeric_limits<ResultType>::max() / -lhs) < -rhs) + return false; + } + } else { + if (lhs < 0) { + if (rhs && lhs < (std::numeric_limits<ResultType>::min() / rhs)) + return false; + } else { + if (lhs && rhs < (std::numeric_limits<ResultType>::min() / lhs)) + return false; + } + } + result = lhs * rhs; + return true; + } + + static inline bool equals(LHS lhs, RHS rhs) { return lhs == rhs; } + +}; + +template <typename LHS, typename RHS, typename ResultType> struct ArithmeticOperations<LHS, RHS, ResultType, false, false> { + // LHS and RHS are unsigned types so bounds checks are nice and easy + static inline bool add(LHS lhs, RHS rhs, ResultType& result) WARN_UNUSED_RETURN + { + ResultType temp = lhs + rhs; + if (temp < lhs) + return false; + result = temp; + return true; + } + + static inline bool sub(LHS lhs, RHS rhs, ResultType& result) WARN_UNUSED_RETURN + { + ResultType temp = lhs - rhs; + if (temp > lhs) + return false; + result = temp; + return true; + } + + static inline bool multiply(LHS lhs, RHS rhs, ResultType& result) WARN_UNUSED_RETURN + { + ResultType temp = lhs * rhs; + if (temp < lhs) + return false; + result = temp; + return true; + } + + static inline bool equals(LHS lhs, RHS rhs) { return lhs == rhs; } + +}; + +template <typename ResultType> struct ArithmeticOperations<int, unsigned, ResultType, true, false> { + static inline bool add(int64_t lhs, int64_t rhs, ResultType& result) + { + int64_t temp = lhs + rhs; + if (temp < std::numeric_limits<ResultType>::min()) + return false; + if (temp > std::numeric_limits<ResultType>::max()) + return false; + result = static_cast<ResultType>(temp); + return true; + } + + static inline bool sub(int64_t lhs, int64_t rhs, ResultType& result) + { + int64_t temp = lhs - rhs; + if (temp < std::numeric_limits<ResultType>::min()) + return false; + if (temp > std::numeric_limits<ResultType>::max()) + return false; + result = static_cast<ResultType>(temp); + return true; + } + + static inline bool multiply(int64_t lhs, int64_t rhs, ResultType& result) + { + int64_t temp = lhs * rhs; + if (temp < std::numeric_limits<ResultType>::min()) + return false; + if (temp > std::numeric_limits<ResultType>::max()) + return false; + result = static_cast<ResultType>(temp); + return true; + } + + static inline bool equals(int lhs, unsigned rhs) + { + return static_cast<int64_t>(lhs) == static_cast<int64_t>(rhs); + } +}; + +template <typename ResultType> struct ArithmeticOperations<unsigned, int, ResultType, false, true> { + static inline bool add(int64_t lhs, int64_t rhs, ResultType& result) + { + return ArithmeticOperations<int, unsigned, ResultType>::add(rhs, lhs, result); + } + + static inline bool sub(int64_t lhs, int64_t rhs, ResultType& result) + { + return ArithmeticOperations<int, unsigned, ResultType>::sub(lhs, rhs, result); + } + + static inline bool multiply(int64_t lhs, int64_t rhs, ResultType& result) + { + return ArithmeticOperations<int, unsigned, ResultType>::multiply(rhs, lhs, result); + } + + static inline bool equals(unsigned lhs, int rhs) + { + return ArithmeticOperations<int, unsigned, ResultType>::equals(rhs, lhs); + } +}; + +template <typename U, typename V, typename R> static inline bool safeAdd(U lhs, V rhs, R& result) +{ + return ArithmeticOperations<U, V, R>::add(lhs, rhs, result); +} + +template <typename U, typename V, typename R> static inline bool safeSub(U lhs, V rhs, R& result) +{ + return ArithmeticOperations<U, V, R>::sub(lhs, rhs, result); +} + +template <typename U, typename V, typename R> static inline bool safeMultiply(U lhs, V rhs, R& result) +{ + return ArithmeticOperations<U, V, R>::multiply(lhs, rhs, result); +} + +template <typename U, typename V> static inline bool safeEquals(U lhs, V rhs) +{ + return ArithmeticOperations<U, V>::equals(lhs, rhs); +} + +enum ResultOverflowedTag { ResultOverflowed }; + +// FIXME: Needed to workaround http://llvm.org/bugs/show_bug.cgi?id=10801 +static inline bool workAroundClangBug() { return true; } + +template <typename T, class OverflowHandler> class Checked : public OverflowHandler { +public: + template <typename _T, class _OverflowHandler> friend class Checked; + Checked() + : m_value(0) + { + } + + Checked(ResultOverflowedTag) + : m_value(0) + { + // FIXME: Remove this when clang fixes http://llvm.org/bugs/show_bug.cgi?id=10801 + if (workAroundClangBug()) + this->overflowed(); + } + + template <typename U> Checked(U value) + { + if (!isInBounds<T>(value)) + this->overflowed(); + m_value = static_cast<T>(value); + } + + template <typename V> Checked(const Checked<T, V>& rhs) + : m_value(rhs.m_value) + { + if (rhs.hasOverflowed()) + this->overflowed(); + } + + template <typename U> Checked(const Checked<U, OverflowHandler>& rhs) + : OverflowHandler(rhs) + { + if (!isInBounds<T>(rhs.m_value)) + this->overflowed(); + m_value = static_cast<T>(rhs.m_value); + } + + template <typename U, typename V> Checked(const Checked<U, V>& rhs) + { + if (rhs.hasOverflowed()) + this->overflowed(); + if (!isInBounds<T>(rhs.m_value)) + this->overflowed(); + m_value = static_cast<T>(rhs.m_value); + } + + const Checked& operator=(Checked rhs) + { + this->clearOverflow(); + if (rhs.hasOverflowed()) + this->overflowed(); + m_value = static_cast<T>(rhs.m_value); + return *this; + } + + template <typename U> const Checked& operator=(U value) + { + return *this = Checked(value); + } + + template <typename U, typename V> const Checked& operator=(const Checked<U, V>& rhs) + { + return *this = Checked(rhs); + } + + // prefix + const Checked& operator++() + { + if (m_value == std::numeric_limits<T>::max()) + this->overflowed(); + m_value++; + return *this; + } + + const Checked& operator--() + { + if (m_value == std::numeric_limits<T>::min()) + this->overflowed(); + m_value--; + return *this; + } + + // postfix operators + const Checked operator++(int) + { + if (m_value == std::numeric_limits<T>::max()) + this->overflowed(); + return Checked(m_value++); + } + + const Checked operator--(int) + { + if (m_value == std::numeric_limits<T>::min()) + this->overflowed(); + return Checked(m_value--); + } + + // Boolean operators + bool operator!() const + { + if (this->hasOverflowed()) + CRASH(); + return !m_value; + } + + typedef void* (Checked::*UnspecifiedBoolType); + operator UnspecifiedBoolType*() const + { + if (this->hasOverflowed()) + CRASH(); + return (m_value) ? reinterpret_cast<UnspecifiedBoolType*>(1) : 0; + } + + // Value accessors. unsafeGet() will crash if there's been an overflow. + T unsafeGet() const + { + if (this->hasOverflowed()) + CRASH(); + return m_value; + } + + bool safeGet(T& value) const WARN_UNUSED_RETURN + { + value = m_value; + return this->hasOverflowed(); + } + + // Mutating assignment + template <typename U> const Checked operator+=(U rhs) + { + if (!safeAdd(m_value, rhs, m_value)) + this->overflowed(); + return *this; + } + + template <typename U> const Checked operator-=(U rhs) + { + if (!safeSub(m_value, rhs, m_value)) + this->overflowed(); + return *this; + } + + template <typename U> const Checked operator*=(U rhs) + { + if (!safeMultiply(m_value, rhs, m_value)) + this->overflowed(); + return *this; + } + + template <typename U, typename V> const Checked operator+=(Checked<U, V> rhs) + { + if (rhs.hasOverflowed()) + this->overflowed(); + return *this += rhs.m_value; + } + + template <typename U, typename V> const Checked operator-=(Checked<U, V> rhs) + { + if (rhs.hasOverflowed()) + this->overflowed(); + return *this -= rhs.m_value; + } + + template <typename U, typename V> const Checked operator*=(Checked<U, V> rhs) + { + if (rhs.hasOverflowed()) + this->overflowed(); + return *this *= rhs.m_value; + } + + // Equality comparisons + template <typename V> bool operator==(Checked<T, V> rhs) + { + return unsafeGet() == rhs.unsafeGet(); + } + + template <typename U> bool operator==(U rhs) + { + if (this->hasOverflowed()) + this->overflowed(); + return safeEquals(m_value, rhs); + } + + template <typename U, typename V> const Checked operator==(Checked<U, V> rhs) + { + return unsafeGet() == Checked(rhs.unsafeGet()); + } + + template <typename U> bool operator!=(U rhs) + { + return !(*this == rhs); + } + +private: + // Disallow implicit conversion of floating point to integer types + Checked(float); + Checked(double); + void operator=(float); + void operator=(double); + void operator+=(float); + void operator+=(double); + void operator-=(float); + void operator-=(double); + T m_value; +}; + +template <typename U, typename V, typename OverflowHandler> static inline Checked<typename Result<U, V>::ResultType, OverflowHandler> operator+(Checked<U, OverflowHandler> lhs, Checked<V, OverflowHandler> rhs) +{ + U x = 0; + V y = 0; + bool overflowed = lhs.safeGet(x) || rhs.safeGet(y); + typename Result<U, V>::ResultType result = 0; + overflowed |= !safeAdd(x, y, result); + if (overflowed) + return ResultOverflowed; + return result; +} + +template <typename U, typename V, typename OverflowHandler> static inline Checked<typename Result<U, V>::ResultType, OverflowHandler> operator-(Checked<U, OverflowHandler> lhs, Checked<V, OverflowHandler> rhs) +{ + U x = 0; + V y = 0; + bool overflowed = lhs.safeGet(x) || rhs.safeGet(y); + typename Result<U, V>::ResultType result = 0; + overflowed |= !safeSub(x, y, result); + if (overflowed) + return ResultOverflowed; + return result; +} + +template <typename U, typename V, typename OverflowHandler> static inline Checked<typename Result<U, V>::ResultType, OverflowHandler> operator*(Checked<U, OverflowHandler> lhs, Checked<V, OverflowHandler> rhs) +{ + U x = 0; + V y = 0; + bool overflowed = lhs.safeGet(x) || rhs.safeGet(y); + typename Result<U, V>::ResultType result = 0; + overflowed |= !safeMultiply(x, y, result); + if (overflowed) + return ResultOverflowed; + return result; +} + +template <typename U, typename V, typename OverflowHandler> static inline Checked<typename Result<U, V>::ResultType, OverflowHandler> operator+(Checked<U, OverflowHandler> lhs, V rhs) +{ + return lhs + Checked<V, OverflowHandler>(rhs); +} + +template <typename U, typename V, typename OverflowHandler> static inline Checked<typename Result<U, V>::ResultType, OverflowHandler> operator-(Checked<U, OverflowHandler> lhs, V rhs) +{ + return lhs - Checked<V, OverflowHandler>(rhs); +} + +template <typename U, typename V, typename OverflowHandler> static inline Checked<typename Result<U, V>::ResultType, OverflowHandler> operator*(Checked<U, OverflowHandler> lhs, V rhs) +{ + return lhs * Checked<V, OverflowHandler>(rhs); +} + +template <typename U, typename V, typename OverflowHandler> static inline Checked<typename Result<U, V>::ResultType, OverflowHandler> operator+(U lhs, Checked<V, OverflowHandler> rhs) +{ + return Checked<U, OverflowHandler>(lhs) + rhs; +} + +template <typename U, typename V, typename OverflowHandler> static inline Checked<typename Result<U, V>::ResultType, OverflowHandler> operator-(U lhs, Checked<V, OverflowHandler> rhs) +{ + return Checked<U, OverflowHandler>(lhs) - rhs; +} + +template <typename U, typename V, typename OverflowHandler> static inline Checked<typename Result<U, V>::ResultType, OverflowHandler> operator*(U lhs, Checked<V, OverflowHandler> rhs) +{ + return Checked<U, OverflowHandler>(lhs) * rhs; +} + +} + +using WTF::Checked; +using WTF::RecordOverflow; + +#endif diff --git a/Source/JavaScriptCore/wtf/Compiler.h b/Source/JavaScriptCore/wtf/Compiler.h new file mode 100644 index 000000000..93d3316bf --- /dev/null +++ b/Source/JavaScriptCore/wtf/Compiler.h @@ -0,0 +1,242 @@ +/* + * Copyright (C) 2011 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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 APPLE INC. ``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 APPLE INC. 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. + */ + +#ifndef WTF_Compiler_h +#define WTF_Compiler_h + +/* COMPILER() - the compiler being used to build the project */ +#define COMPILER(WTF_FEATURE) (defined WTF_COMPILER_##WTF_FEATURE && WTF_COMPILER_##WTF_FEATURE) + +/* COMPILER_SUPPORTS() - whether the compiler being used to build the project supports the given feature. */ +#define COMPILER_SUPPORTS(WTF_COMPILER_FEATURE) (defined WTF_COMPILER_SUPPORTS_##WTF_COMPILER_FEATURE && WTF_COMPILER_SUPPORTS_##WTF_COMPILER_FEATURE) + +/* ==== COMPILER() - the compiler being used to build the project ==== */ + +/* COMPILER(CLANG) - Clang */ +#if defined(__clang__) +#define WTF_COMPILER_CLANG 1 + +#ifndef __has_extension +#define __has_extension __has_feature /* Compatibility with older versions of clang */ +#endif + +/* Specific compiler features */ +#define WTF_COMPILER_SUPPORTS_CXX_VARIADIC_TEMPLATES __has_feature(cxx_variadic_templates) +#define WTF_COMPILER_SUPPORTS_CXX_RVALUE_REFERENCES __has_feature(cxx_rvalue_references) +#define WTF_COMPILER_SUPPORTS_CXX_DELETED_FUNCTIONS __has_feature(cxx_deleted_functions) +#define WTF_COMPILER_SUPPORTS_CXX_NULLPTR __has_feature(cxx_nullptr) +#define WTF_COMPILER_SUPPORTS_BLOCKS __has_feature(blocks) + +#endif + +/* COMPILER(MSVC) - Microsoft Visual C++ */ +/* COMPILER(MSVC7_OR_LOWER) - Microsoft Visual C++ 2003 or lower*/ +/* COMPILER(MSVC9_OR_LOWER) - Microsoft Visual C++ 2008 or lower*/ +#if defined(_MSC_VER) +#define WTF_COMPILER_MSVC 1 +#if _MSC_VER < 1400 +#define WTF_COMPILER_MSVC7_OR_LOWER 1 +#elif _MSC_VER < 1600 +#define WTF_COMPILER_MSVC9_OR_LOWER 1 +#endif + +/* Specific compiler features */ +#if _MSC_VER >= 1600 +#define WTF_COMPILER_SUPPORTS_CXX_NULLPTR 1 +#endif + +#endif + +/* COMPILER(RVCT) - ARM RealView Compilation Tools */ +/* COMPILER(RVCT4_OR_GREATER) - ARM RealView Compilation Tools 4.0 or greater */ +#if defined(__CC_ARM) || defined(__ARMCC__) +#define WTF_COMPILER_RVCT 1 +#define RVCT_VERSION_AT_LEAST(major, minor, patch, build) (__ARMCC_VERSION >= (major * 100000 + minor * 10000 + patch * 1000 + build)) +#else +/* Define this for !RVCT compilers, just so we can write things like RVCT_VERSION_AT_LEAST(3, 0, 0, 0). */ +#define RVCT_VERSION_AT_LEAST(major, minor, patch, build) 0 +#endif + +/* COMPILER(GCCE) - GNU Compiler Collection for Embedded */ +#if defined(__GCCE__) +#define WTF_COMPILER_GCCE 1 +#define GCCE_VERSION (__GCCE__ * 10000 + __GCCE_MINOR__ * 100 + __GCCE_PATCHLEVEL__) +#define GCCE_VERSION_AT_LEAST(major, minor, patch) (GCCE_VERSION >= (major * 10000 + minor * 100 + patch)) +#endif + +/* COMPILER(GCC) - GNU Compiler Collection */ +/* --gnu option of the RVCT compiler also defines __GNUC__ */ +#if defined(__GNUC__) && !COMPILER(RVCT) +#define WTF_COMPILER_GCC 1 +#define GCC_VERSION (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) +#define GCC_VERSION_AT_LEAST(major, minor, patch) (GCC_VERSION >= (major * 10000 + minor * 100 + patch)) + +/* Specific compiler features */ +#if !COMPILER(CLANG) && GCC_VERSION_AT_LEAST(4, 6, 0) && defined(__GXX_EXPERIMENTAL_CXX0X__) +#define WTF_COMPILER_SUPPORTS_CXX_NULLPTR 1 +#endif + +#else +/* Define this for !GCC compilers, just so we can write things like GCC_VERSION_AT_LEAST(4, 1, 0). */ +#define GCC_VERSION_AT_LEAST(major, minor, patch) 0 +#endif + +/* COMPILER(MINGW) - MinGW GCC */ +/* COMPILER(MINGW64) - mingw-w64 GCC - only used as additional check to exclude mingw.org specific functions */ +#if defined(__MINGW32__) +#define WTF_COMPILER_MINGW 1 +#include <_mingw.h> /* private MinGW header */ + #if defined(__MINGW64_VERSION_MAJOR) /* best way to check for mingw-w64 vs mingw.org */ + #define WTF_COMPILER_MINGW64 1 + #endif /* __MINGW64_VERSION_MAJOR */ +#endif /* __MINGW32__ */ + +/* COMPILER(INTEL) - Intel C++ Compiler */ +#if defined(__INTEL_COMPILER) +#define WTF_COMPILER_INTEL 1 +#endif + +/* COMPILER(SUNCC) */ +#if defined(__SUNPRO_CC) || defined(__SUNPRO_C) +#define WTF_COMPILER_SUNCC 1 +#endif + +/* ==== Compiler features ==== */ + + +/* ALWAYS_INLINE */ + +#ifndef ALWAYS_INLINE +#if COMPILER(GCC) && defined(NDEBUG) && !COMPILER(MINGW) +#define ALWAYS_INLINE inline __attribute__((__always_inline__)) +#elif (COMPILER(MSVC) || COMPILER(RVCT)) && defined(NDEBUG) +#define ALWAYS_INLINE __forceinline +#else +#define ALWAYS_INLINE inline +#endif +#endif + + +/* NEVER_INLINE */ + +#ifndef NEVER_INLINE +#if COMPILER(GCC) +#define NEVER_INLINE __attribute__((__noinline__)) +#elif COMPILER(RVCT) +#define NEVER_INLINE __declspec(noinline) +#else +#define NEVER_INLINE +#endif +#endif + + +/* UNLIKELY */ + +#ifndef UNLIKELY +#if COMPILER(GCC) || (RVCT_VERSION_AT_LEAST(3, 0, 0, 0) && defined(__GNUC__)) +#define UNLIKELY(x) __builtin_expect((x), 0) +#else +#define UNLIKELY(x) (x) +#endif +#endif + + +/* LIKELY */ + +#ifndef LIKELY +#if COMPILER(GCC) || (RVCT_VERSION_AT_LEAST(3, 0, 0, 0) && defined(__GNUC__)) +#define LIKELY(x) __builtin_expect((x), 1) +#else +#define LIKELY(x) (x) +#endif +#endif + + +/* NO_RETURN */ + + +#ifndef NO_RETURN +#if COMPILER(GCC) +#define NO_RETURN __attribute((__noreturn__)) +#elif COMPILER(MSVC) || COMPILER(RVCT) +#define NO_RETURN __declspec(noreturn) +#else +#define NO_RETURN +#endif +#endif + + +/* NO_RETURN_WITH_VALUE */ + +#ifndef NO_RETURN_WITH_VALUE +#if !COMPILER(MSVC) +#define NO_RETURN_WITH_VALUE NO_RETURN +#else +#define NO_RETURN_WITH_VALUE +#endif +#endif + + +/* WARN_UNUSED_RETURN */ + +#if COMPILER(GCC) +#define WARN_UNUSED_RETURN __attribute__ ((warn_unused_result)) +#else +#define WARN_UNUSED_RETURN +#endif + +/* OVERRIDE */ + +#ifndef OVERRIDE +#if COMPILER(CLANG) +#if __has_extension(cxx_override_control) +#define OVERRIDE override +#endif +#elif COMPILER(MSVC) +#define OVERRIDE override +#endif +#endif + +#ifndef OVERRIDE +#define OVERRIDE +#endif + +/* FINAL */ + +#ifndef FINAL +#if COMPILER(CLANG) +#if __has_extension(cxx_override_control) +#define FINAL final +#endif +#elif COMPILER(MSVC) +#define FINAL sealed +#endif +#endif + +#ifndef FINAL +#define FINAL +#endif + +#endif /* WTF_Compiler_h */ diff --git a/Source/JavaScriptCore/wtf/Complex.h b/Source/JavaScriptCore/wtf/Complex.h new file mode 100644 index 000000000..40fe56a7b --- /dev/null +++ b/Source/JavaScriptCore/wtf/Complex.h @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of Apple Computer, Inc. ("Apple") 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 APPLE AND ITS 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 APPLE OR ITS 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. + */ + +#ifndef WTF_Complex_h +#define WTF_Complex_h + +#include <complex> +#include <wtf/MathExtras.h> + +namespace WTF { + +typedef std::complex<double> Complex; + +inline Complex complexFromMagnitudePhase(double magnitude, double phase) +{ + return Complex(magnitude * cos(phase), magnitude * sin(phase)); +} + +} // namespace WTF + +using WTF::Complex; +using WTF::complexFromMagnitudePhase; + +#endif // WTF_Complex_h diff --git a/Source/JavaScriptCore/wtf/CryptographicallyRandomNumber.cpp b/Source/JavaScriptCore/wtf/CryptographicallyRandomNumber.cpp new file mode 100644 index 000000000..8c16f5314 --- /dev/null +++ b/Source/JavaScriptCore/wtf/CryptographicallyRandomNumber.cpp @@ -0,0 +1,180 @@ +/* + * Copyright (c) 1996, David Mazieres <dm@uun.org> + * Copyright (c) 2008, Damien Miller <djm@openbsd.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * Arc4 random number generator for OpenBSD. + * + * This code is derived from section 17.1 of Applied Cryptography, + * second edition, which describes a stream cipher allegedly + * compatible with RSA Labs "RC4" cipher (the actual description of + * which is a trade secret). The same algorithm is used as a stream + * cipher called "arcfour" in Tatu Ylonen's ssh package. + * + * RC4 is a registered trademark of RSA Laboratories. + */ + +#include "config.h" +#include "CryptographicallyRandomNumber.h" + +#include "OSRandomSource.h" +#include "StdLibExtras.h" +#include "ThreadingPrimitives.h" + +namespace WTF { + +#if USE(OS_RANDOMNESS) + +namespace { + +class ARC4Stream { +public: + ARC4Stream(); + + uint8_t i; + uint8_t j; + uint8_t s[256]; +}; + +class ARC4RandomNumberGenerator { +public: + ARC4RandomNumberGenerator(); + + uint32_t randomNumber(); + void randomValues(void* buffer, size_t length); + +private: + inline void addRandomData(unsigned char *data, int length); + void stir(); + void stirIfNeeded(); + inline uint8_t getByte(); + inline uint32_t getWord(); + + ARC4Stream m_stream; + int m_count; + Mutex m_mutex; +}; + +ARC4Stream::ARC4Stream() +{ + for (int n = 0; n < 256; n++) + s[n] = n; + i = 0; + j = 0; +} + +ARC4RandomNumberGenerator::ARC4RandomNumberGenerator() + : m_count(0) +{ +} + +void ARC4RandomNumberGenerator::addRandomData(unsigned char* data, int length) +{ + m_stream.i--; + for (int n = 0; n < 256; n++) { + m_stream.i++; + uint8_t si = m_stream.s[m_stream.i]; + m_stream.j += si + data[n % length]; + m_stream.s[m_stream.i] = m_stream.s[m_stream.j]; + m_stream.s[m_stream.j] = si; + } + m_stream.j = m_stream.i; +} + +void ARC4RandomNumberGenerator::stir() +{ + unsigned char randomness[128]; + size_t length = sizeof(randomness); + cryptographicallyRandomValuesFromOS(randomness, length); + addRandomData(randomness, length); + + // Discard early keystream, as per recommendations in: + // http://www.wisdom.weizmann.ac.il/~itsik/RC4/Papers/Rc4_ksa.ps + for (int i = 0; i < 256; i++) + getByte(); + m_count = 1600000; +} + +void ARC4RandomNumberGenerator::stirIfNeeded() +{ + if (m_count <= 0) + stir(); +} + +uint8_t ARC4RandomNumberGenerator::getByte() +{ + m_stream.i++; + uint8_t si = m_stream.s[m_stream.i]; + m_stream.j += si; + uint8_t sj = m_stream.s[m_stream.j]; + m_stream.s[m_stream.i] = sj; + m_stream.s[m_stream.j] = si; + return (m_stream.s[(si + sj) & 0xff]); +} + +uint32_t ARC4RandomNumberGenerator::getWord() +{ + uint32_t val; + val = getByte() << 24; + val |= getByte() << 16; + val |= getByte() << 8; + val |= getByte(); + return val; +} + +uint32_t ARC4RandomNumberGenerator::randomNumber() +{ + MutexLocker locker(m_mutex); + + m_count -= 4; + stirIfNeeded(); + return getWord(); +} + +void ARC4RandomNumberGenerator::randomValues(void* buffer, size_t length) +{ + MutexLocker locker(m_mutex); + + unsigned char* result = reinterpret_cast<unsigned char*>(buffer); + stirIfNeeded(); + while (length--) { + m_count--; + stirIfNeeded(); + result[length] = getByte(); + } +} + +ARC4RandomNumberGenerator& sharedRandomNumberGenerator() +{ + DEFINE_STATIC_LOCAL(ARC4RandomNumberGenerator, randomNumberGenerator, ()); + return randomNumberGenerator; +} + +} + +uint32_t cryptographicallyRandomNumber() +{ + return sharedRandomNumberGenerator().randomNumber(); +} + +void cryptographicallyRandomValues(void* buffer, size_t length) +{ + sharedRandomNumberGenerator().randomValues(buffer, length); +} + +#endif + +} diff --git a/Source/JavaScriptCore/wtf/CryptographicallyRandomNumber.h b/Source/JavaScriptCore/wtf/CryptographicallyRandomNumber.h new file mode 100644 index 000000000..348242e25 --- /dev/null +++ b/Source/JavaScriptCore/wtf/CryptographicallyRandomNumber.h @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2008 Torch Mobile Inc. All rights reserved. (http://www.torchmobile.com/) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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 APPLE COMPUTER, INC. ``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 APPLE COMPUTER, INC. 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. + */ + +#ifndef WTF_CryptographicallyRandomNumber_h +#define WTF_CryptographicallyRandomNumber_h + +#include <stdint.h> + +namespace WTF { + +#if USE(OS_RANDOMNESS) +uint32_t cryptographicallyRandomNumber(); +void cryptographicallyRandomValues(void* buffer, size_t length); +#endif + +} + +#if USE(OS_RANDOMNESS) +using WTF::cryptographicallyRandomNumber; +using WTF::cryptographicallyRandomValues; +#endif + +#endif diff --git a/Source/JavaScriptCore/wtf/CurrentTime.cpp b/Source/JavaScriptCore/wtf/CurrentTime.cpp new file mode 100644 index 000000000..c8c77f54f --- /dev/null +++ b/Source/JavaScriptCore/wtf/CurrentTime.cpp @@ -0,0 +1,347 @@ +/* + * Copyright (C) 2006, 2010 Apple Inc. All rights reserved. + * Copyright (C) 2008 Google Inc. All rights reserved. + * Copyright (C) 2007-2009 Torch Mobile, Inc. + * + * 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 Google Inc. 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 "config.h" +#include "CurrentTime.h" + +#if PLATFORM(MAC) +#include <mach/mach_time.h> +#include <sys/time.h> +#elif OS(WINDOWS) + +// Windows is first since we want to use hires timers, despite USE(CF) +// being defined. +// If defined, WIN32_LEAN_AND_MEAN disables timeBeginPeriod/timeEndPeriod. +#undef WIN32_LEAN_AND_MEAN +#include <windows.h> +#include <math.h> +#include <stdint.h> +#include <time.h> + +#if USE(QUERY_PERFORMANCE_COUNTER) +#if OS(WINCE) +extern "C" time_t mktime(struct tm *t); +#else +#include <sys/timeb.h> +#include <sys/types.h> +#endif +#endif + +#elif PLATFORM(GTK) +#include <glib.h> +#elif PLATFORM(WX) +#include <wx/datetime.h> +#elif PLATFORM(EFL) +#include <Ecore.h> +#else +#include <sys/time.h> +#endif + +#if PLATFORM(QT) +#include <QElapsedTimer> +#endif + +#if PLATFORM(CHROMIUM) +#error Chromium uses a different timer implementation +#endif + +namespace WTF { + +const double msPerSecond = 1000.0; + +#if OS(WINDOWS) + +#if USE(QUERY_PERFORMANCE_COUNTER) + +static LARGE_INTEGER qpcFrequency; +static bool syncedTime; + +static double highResUpTime() +{ + // We use QPC, but only after sanity checking its result, due to bugs: + // http://support.microsoft.com/kb/274323 + // http://support.microsoft.com/kb/895980 + // http://msdn.microsoft.com/en-us/library/ms644904.aspx ("...you can get different results on different processors due to bugs in the basic input/output system (BIOS) or the hardware abstraction layer (HAL)." + + static LARGE_INTEGER qpcLast; + static DWORD tickCountLast; + static bool inited; + + LARGE_INTEGER qpc; + QueryPerformanceCounter(&qpc); + DWORD tickCount = GetTickCount(); + + if (inited) { + __int64 qpcElapsed = ((qpc.QuadPart - qpcLast.QuadPart) * 1000) / qpcFrequency.QuadPart; + __int64 tickCountElapsed; + if (tickCount >= tickCountLast) + tickCountElapsed = (tickCount - tickCountLast); + else { +#if COMPILER(MINGW) + __int64 tickCountLarge = tickCount + 0x100000000ULL; +#else + __int64 tickCountLarge = tickCount + 0x100000000I64; +#endif + tickCountElapsed = tickCountLarge - tickCountLast; + } + + // force a re-sync if QueryPerformanceCounter differs from GetTickCount by more than 500ms. + // (500ms value is from http://support.microsoft.com/kb/274323) + __int64 diff = tickCountElapsed - qpcElapsed; + if (diff > 500 || diff < -500) + syncedTime = false; + } else + inited = true; + + qpcLast = qpc; + tickCountLast = tickCount; + + return (1000.0 * qpc.QuadPart) / static_cast<double>(qpcFrequency.QuadPart); +} + +static double lowResUTCTime() +{ +#if OS(WINCE) + SYSTEMTIME systemTime; + GetSystemTime(&systemTime); + struct tm tmtime; + tmtime.tm_year = systemTime.wYear - 1900; + tmtime.tm_mon = systemTime.wMonth - 1; + tmtime.tm_mday = systemTime.wDay; + tmtime.tm_wday = systemTime.wDayOfWeek; + tmtime.tm_hour = systemTime.wHour; + tmtime.tm_min = systemTime.wMinute; + tmtime.tm_sec = systemTime.wSecond; + time_t timet = mktime(&tmtime); + return timet * msPerSecond + systemTime.wMilliseconds; +#else + struct _timeb timebuffer; + _ftime(&timebuffer); + return timebuffer.time * msPerSecond + timebuffer.millitm; +#endif +} + +static bool qpcAvailable() +{ + static bool available; + static bool checked; + + if (checked) + return available; + + available = QueryPerformanceFrequency(&qpcFrequency); + checked = true; + return available; +} + +double currentTime() +{ + // Use a combination of ftime and QueryPerformanceCounter. + // ftime returns the information we want, but doesn't have sufficient resolution. + // QueryPerformanceCounter has high resolution, but is only usable to measure time intervals. + // To combine them, we call ftime and QueryPerformanceCounter initially. Later calls will use QueryPerformanceCounter + // by itself, adding the delta to the saved ftime. We periodically re-sync to correct for drift. + static double syncLowResUTCTime; + static double syncHighResUpTime; + static double lastUTCTime; + + double lowResTime = lowResUTCTime(); + + if (!qpcAvailable()) + return lowResTime / 1000.0; + + double highResTime = highResUpTime(); + + if (!syncedTime) { + timeBeginPeriod(1); // increase time resolution around low-res time getter + syncLowResUTCTime = lowResTime = lowResUTCTime(); + timeEndPeriod(1); // restore time resolution + syncHighResUpTime = highResTime; + syncedTime = true; + } + + double highResElapsed = highResTime - syncHighResUpTime; + double utc = syncLowResUTCTime + highResElapsed; + + // force a clock re-sync if we've drifted + double lowResElapsed = lowResTime - syncLowResUTCTime; + const double maximumAllowedDriftMsec = 15.625 * 2.0; // 2x the typical low-res accuracy + if (fabs(highResElapsed - lowResElapsed) > maximumAllowedDriftMsec) + syncedTime = false; + + // make sure time doesn't run backwards (only correct if difference is < 2 seconds, since DST or clock changes could occur) + const double backwardTimeLimit = 2000.0; + if (utc < lastUTCTime && (lastUTCTime - utc) < backwardTimeLimit) + return lastUTCTime / 1000.0; + lastUTCTime = utc; + return utc / 1000.0; +} + +#else + +static double currentSystemTime() +{ + FILETIME ft; + GetCurrentFT(&ft); + + // As per Windows documentation for FILETIME, copy the resulting FILETIME structure to a + // ULARGE_INTEGER structure using memcpy (using memcpy instead of direct assignment can + // prevent alignment faults on 64-bit Windows). + + ULARGE_INTEGER t; + memcpy(&t, &ft, sizeof(t)); + + // Windows file times are in 100s of nanoseconds. + // To convert to seconds, we have to divide by 10,000,000, which is more quickly + // done by multiplying by 0.0000001. + + // Between January 1, 1601 and January 1, 1970, there were 369 complete years, + // of which 89 were leap years (1700, 1800, and 1900 were not leap years). + // That is a total of 134774 days, which is 11644473600 seconds. + + return t.QuadPart * 0.0000001 - 11644473600.0; +} + +double currentTime() +{ + static bool init = false; + static double lastTime; + static DWORD lastTickCount; + if (!init) { + lastTime = currentSystemTime(); + lastTickCount = GetTickCount(); + init = true; + return lastTime; + } + + DWORD tickCountNow = GetTickCount(); + DWORD elapsed = tickCountNow - lastTickCount; + double timeNow = lastTime + (double)elapsed / 1000.; + if (elapsed >= 0x7FFFFFFF) { + lastTime = timeNow; + lastTickCount = tickCountNow; + } + return timeNow; +} + +#endif // USE(QUERY_PERFORMANCE_COUNTER) + +#elif PLATFORM(GTK) + +// Note: GTK on Windows will pick up the PLATFORM(WIN) implementation above which provides +// better accuracy compared with Windows implementation of g_get_current_time: +// (http://www.google.com/codesearch/p?hl=en#HHnNRjks1t0/glib-2.5.2/glib/gmain.c&q=g_get_current_time). +// Non-Windows GTK builds could use gettimeofday() directly but for the sake of consistency lets use GTK function. +double currentTime() +{ + GTimeVal now; + g_get_current_time(&now); + return static_cast<double>(now.tv_sec) + static_cast<double>(now.tv_usec / 1000000.0); +} + +#elif PLATFORM(WX) + +double currentTime() +{ + wxDateTime now = wxDateTime::UNow(); + return (double)now.GetTicks() + (double)(now.GetMillisecond() / 1000.0); +} + +#elif PLATFORM(EFL) + +double currentTime() +{ + return ecore_time_unix_get(); +} + +#else + +double currentTime() +{ + struct timeval now; + gettimeofday(&now, 0); + return now.tv_sec + now.tv_usec / 1000000.0; +} + +#endif + +#if PLATFORM(MAC) + +double monotonicallyIncreasingTime() +{ + // Based on listing #2 from Apple QA 1398. + static mach_timebase_info_data_t timebaseInfo; + if (!timebaseInfo.denom) { + kern_return_t kr = mach_timebase_info(&timebaseInfo); + ASSERT_UNUSED(kr, kr == KERN_SUCCESS); + } + return (mach_absolute_time() * timebaseInfo.numer) / (1.0e9 * timebaseInfo.denom); +} + +#elif PLATFORM(EFL) + +double monotonicallyIncreasingTime() +{ + return ecore_time_get(); +} + +#elif PLATFORM(GTK) + +double monotonicallyIncreasingTime() +{ + return static_cast<double>(g_get_monotonic_time() / 1000000.0); +} + +#elif PLATFORM(QT) + +double monotonicallyIncreasingTime() +{ + ASSERT(QElapsedTimer::isMonotonic()); + static QElapsedTimer timer; + return timer.nsecsElapsed() / 1.0e9; +} + +#else + +double monotonicallyIncreasingTime() +{ + static double lastTime = 0; + double currentTimeNow = currentTime(); + if (currentTimeNow < lastTime) + return lastTime; + lastTime = currentTimeNow; + return currentTimeNow; +} + +#endif + +} // namespace WTF diff --git a/Source/JavaScriptCore/wtf/CurrentTime.h b/Source/JavaScriptCore/wtf/CurrentTime.h new file mode 100644 index 000000000..71775c273 --- /dev/null +++ b/Source/JavaScriptCore/wtf/CurrentTime.h @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2006 Apple Computer, Inc. All rights reserved. + * Copyright (C) 2008 Google Inc. 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 Google Inc. 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. + */ + +#ifndef CurrentTime_h +#define CurrentTime_h + +#include <time.h> + +namespace WTF { + +// Returns the current UTC time in seconds, counted from January 1, 1970. +// Precision varies depending on platform but is usually as good or better +// than a millisecond. +double currentTime(); + +// Same thing, in milliseconds. +inline double currentTimeMS() +{ + return currentTime() * 1000.0; +} + +inline void getLocalTime(const time_t* localTime, struct tm* localTM) +{ +#if COMPILER(MSVC7_OR_LOWER) || COMPILER(MINGW) || OS(WINCE) + *localTM = *localtime(localTime); +#elif COMPILER(MSVC) + localtime_s(localTM, localTime); +#else + localtime_r(localTime, localTM); +#endif +} + +// Provides a monotonically increasing time in seconds since an arbitrary point in the past. +// On unsupported platforms, this function only guarantees the result will be non-decreasing. +double monotonicallyIncreasingTime(); + +} // namespace WTF + +using WTF::currentTime; +using WTF::currentTimeMS; +using WTF::getLocalTime; +using WTF::monotonicallyIncreasingTime; + +#endif // CurrentTime_h diff --git a/Source/JavaScriptCore/wtf/DateMath.cpp b/Source/JavaScriptCore/wtf/DateMath.cpp new file mode 100644 index 000000000..86bd99527 --- /dev/null +++ b/Source/JavaScriptCore/wtf/DateMath.cpp @@ -0,0 +1,1062 @@ +/* + * Copyright (C) 1999-2000 Harri Porten (porten@kde.org) + * Copyright (C) 2006, 2007 Apple Inc. All rights reserved. + * Copyright (C) 2009 Google Inc. All rights reserved. + * Copyright (C) 2007-2009 Torch Mobile, Inc. + * Copyright (C) 2010 &yet, LLC. (nate@andyet.net) + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998 + * the Initial Developer. All Rights Reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Alternatively, the contents of this file may be used under the terms + * of either the Mozilla Public License Version 1.1, found at + * http://www.mozilla.org/MPL/ (the "MPL") or the GNU General Public + * License Version 2.0, found at http://www.fsf.org/copyleft/gpl.html + * (the "GPL"), in which case the provisions of the MPL or the GPL are + * applicable instead of those above. If you wish to allow use of your + * version of this file only under the terms of one of those two + * licenses (the MPL or the GPL) and not to allow others to use your + * version of this file under the LGPL, indicate your decision by + * deletingthe provisions above and replace them with the notice and + * other provisions required by the MPL or the GPL, as the case may be. + * If you do not delete the provisions above, a recipient may use your + * version of this file under any of the LGPL, the MPL or the GPL. + + * Copyright 2006-2008 the V8 project authors. 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 Google Inc. 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 "config.h" +#include "DateMath.h" + +#include "Assertions.h" +#include "ASCIICType.h" +#include "CurrentTime.h" +#include "MathExtras.h" +#include "StdLibExtras.h" +#include "StringExtras.h" + +#include <algorithm> +#include <limits.h> +#include <limits> +#include <stdint.h> +#include <time.h> +#include <wtf/text/StringBuilder.h> + +#if HAVE(ERRNO_H) +#include <errno.h> +#endif + +#if OS(WINCE) +extern "C" size_t strftime(char * const s, const size_t maxsize, const char * const format, const struct tm * const t); +extern "C" struct tm * localtime(const time_t *timer); +#endif + +#if HAVE(SYS_TIME_H) +#include <sys/time.h> +#endif + +#if HAVE(SYS_TIMEB_H) +#include <sys/timeb.h> +#endif + +using namespace WTF; + +namespace WTF { + +/* Constants */ + +static const double minutesPerDay = 24.0 * 60.0; +static const double secondsPerDay = 24.0 * 60.0 * 60.0; +static const double secondsPerYear = 24.0 * 60.0 * 60.0 * 365.0; + +static const double usecPerSec = 1000000.0; + +static const double maxUnixTime = 2145859200.0; // 12/31/2037 +// ECMAScript asks not to support for a date of which total +// millisecond value is larger than the following value. +// See 15.9.1.14 of ECMA-262 5th edition. +static const double maxECMAScriptTime = 8.64E15; + +// Day of year for the first day of each month, where index 0 is January, and day 0 is January 1. +// First for non-leap years, then for leap years. +static const int firstDayOfMonth[2][12] = { + {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334}, + {0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335} +}; + +bool isLeapYear(int year) +{ + if (year % 4 != 0) + return false; + if (year % 400 == 0) + return true; + if (year % 100 == 0) + return false; + return true; +} + +static inline int daysInYear(int year) +{ + return 365 + isLeapYear(year); +} + +static inline double daysFrom1970ToYear(int year) +{ + // The Gregorian Calendar rules for leap years: + // Every fourth year is a leap year. 2004, 2008, and 2012 are leap years. + // However, every hundredth year is not a leap year. 1900 and 2100 are not leap years. + // Every four hundred years, there's a leap year after all. 2000 and 2400 are leap years. + + static const int leapDaysBefore1971By4Rule = 1970 / 4; + static const int excludedLeapDaysBefore1971By100Rule = 1970 / 100; + static const int leapDaysBefore1971By400Rule = 1970 / 400; + + const double yearMinusOne = year - 1; + const double yearsToAddBy4Rule = floor(yearMinusOne / 4.0) - leapDaysBefore1971By4Rule; + const double yearsToExcludeBy100Rule = floor(yearMinusOne / 100.0) - excludedLeapDaysBefore1971By100Rule; + const double yearsToAddBy400Rule = floor(yearMinusOne / 400.0) - leapDaysBefore1971By400Rule; + + return 365.0 * (year - 1970) + yearsToAddBy4Rule - yearsToExcludeBy100Rule + yearsToAddBy400Rule; +} + +double msToDays(double ms) +{ + return floor(ms / msPerDay); +} + +static String twoDigitStringFromNumber(int number) +{ + ASSERT(number >= 0 && number < 100); + if (number > 9) + return String::number(number); + return makeString("0", String::number(number)); +} + +int msToYear(double ms) +{ + int approxYear = static_cast<int>(floor(ms / (msPerDay * 365.2425)) + 1970); + double msFromApproxYearTo1970 = msPerDay * daysFrom1970ToYear(approxYear); + if (msFromApproxYearTo1970 > ms) + return approxYear - 1; + if (msFromApproxYearTo1970 + msPerDay * daysInYear(approxYear) <= ms) + return approxYear + 1; + return approxYear; +} + +int dayInYear(double ms, int year) +{ + return static_cast<int>(msToDays(ms) - daysFrom1970ToYear(year)); +} + +static inline double msToMilliseconds(double ms) +{ + double result = fmod(ms, msPerDay); + if (result < 0) + result += msPerDay; + return result; +} + +int msToMinutes(double ms) +{ + double result = fmod(floor(ms / msPerMinute), minutesPerHour); + if (result < 0) + result += minutesPerHour; + return static_cast<int>(result); +} + +int msToHours(double ms) +{ + double result = fmod(floor(ms/msPerHour), hoursPerDay); + if (result < 0) + result += hoursPerDay; + return static_cast<int>(result); +} + +int monthFromDayInYear(int dayInYear, bool leapYear) +{ + const int d = dayInYear; + int step; + + if (d < (step = 31)) + return 0; + step += (leapYear ? 29 : 28); + if (d < step) + return 1; + if (d < (step += 31)) + return 2; + if (d < (step += 30)) + return 3; + if (d < (step += 31)) + return 4; + if (d < (step += 30)) + return 5; + if (d < (step += 31)) + return 6; + if (d < (step += 31)) + return 7; + if (d < (step += 30)) + return 8; + if (d < (step += 31)) + return 9; + if (d < (step += 30)) + return 10; + return 11; +} + +static inline bool checkMonth(int dayInYear, int& startDayOfThisMonth, int& startDayOfNextMonth, int daysInThisMonth) +{ + startDayOfThisMonth = startDayOfNextMonth; + startDayOfNextMonth += daysInThisMonth; + return (dayInYear <= startDayOfNextMonth); +} + +int dayInMonthFromDayInYear(int dayInYear, bool leapYear) +{ + const int d = dayInYear; + int step; + int next = 30; + + if (d <= next) + return d + 1; + const int daysInFeb = (leapYear ? 29 : 28); + if (checkMonth(d, step, next, daysInFeb)) + return d - step; + if (checkMonth(d, step, next, 31)) + return d - step; + if (checkMonth(d, step, next, 30)) + return d - step; + if (checkMonth(d, step, next, 31)) + return d - step; + if (checkMonth(d, step, next, 30)) + return d - step; + if (checkMonth(d, step, next, 31)) + return d - step; + if (checkMonth(d, step, next, 31)) + return d - step; + if (checkMonth(d, step, next, 30)) + return d - step; + if (checkMonth(d, step, next, 31)) + return d - step; + if (checkMonth(d, step, next, 30)) + return d - step; + step = next; + return d - step; +} + +static inline int monthToDayInYear(int month, bool isLeapYear) +{ + return firstDayOfMonth[isLeapYear][month]; +} + +double dateToDaysFrom1970(int year, int month, int day) +{ + year += month / 12; + + month %= 12; + if (month < 0) { + month += 12; + --year; + } + + double yearday = floor(daysFrom1970ToYear(year)); + ASSERT((year >= 1970 && yearday >= 0) || (year < 1970 && yearday < 0)); + int monthday = monthToDayInYear(month, isLeapYear(year)); + + return yearday + monthday + day - 1; +} + +// There is a hard limit at 2038 that we currently do not have a workaround +// for (rdar://problem/5052975). +static inline int maximumYearForDST() +{ + return 2037; +} + +static inline int minimumYearForDST() +{ + // Because of the 2038 issue (see maximumYearForDST) if the current year is + // greater than the max year minus 27 (2010), we want to use the max year + // minus 27 instead, to ensure there is a range of 28 years that all years + // can map to. + return std::min(msToYear(jsCurrentTime()), maximumYearForDST() - 27) ; +} + +/* + * Find an equivalent year for the one given, where equivalence is deterined by + * the two years having the same leapness and the first day of the year, falling + * on the same day of the week. + * + * This function returns a year between this current year and 2037, however this + * function will potentially return incorrect results if the current year is after + * 2010, (rdar://problem/5052975), if the year passed in is before 1900 or after + * 2100, (rdar://problem/5055038). + */ +int equivalentYearForDST(int year) +{ + // It is ok if the cached year is not the current year as long as the rules + // for DST did not change between the two years; if they did the app would need + // to be restarted. + static int minYear = minimumYearForDST(); + int maxYear = maximumYearForDST(); + + int difference; + if (year > maxYear) + difference = minYear - year; + else if (year < minYear) + difference = maxYear - year; + else + return year; + + int quotient = difference / 28; + int product = (quotient) * 28; + + year += product; + ASSERT((year >= minYear && year <= maxYear) || (product - year == static_cast<int>(std::numeric_limits<double>::quiet_NaN()))); + return year; +} + +int32_t calculateUTCOffset() +{ + time_t localTime = time(0); + tm localt; + getLocalTime(&localTime, &localt); + + // Get the difference between this time zone and UTC on the 1st of January of this year. + localt.tm_sec = 0; + localt.tm_min = 0; + localt.tm_hour = 0; + localt.tm_mday = 1; + localt.tm_mon = 0; + // Not setting localt.tm_year! + localt.tm_wday = 0; + localt.tm_yday = 0; + localt.tm_isdst = 0; +#if HAVE(TM_GMTOFF) + localt.tm_gmtoff = 0; +#endif +#if HAVE(TM_ZONE) + localt.tm_zone = 0; +#endif + +#if HAVE(TIMEGM) + time_t utcOffset = timegm(&localt) - mktime(&localt); +#else + // Using a canned date of 01/01/2009 on platforms with weaker date-handling foo. + localt.tm_year = 109; + time_t utcOffset = 1230768000 - mktime(&localt); +#endif + + return static_cast<int32_t>(utcOffset * 1000); +} + +/* + * Get the DST offset for the time passed in. + */ +static double calculateDSTOffsetSimple(double localTimeSeconds, double utcOffset) +{ + if (localTimeSeconds > maxUnixTime) + localTimeSeconds = maxUnixTime; + else if (localTimeSeconds < 0) // Go ahead a day to make localtime work (does not work with 0) + localTimeSeconds += secondsPerDay; + + //input is UTC so we have to shift back to local time to determine DST thus the + getUTCOffset() + double offsetTime = (localTimeSeconds * msPerSecond) + utcOffset; + + // Offset from UTC but doesn't include DST obviously + int offsetHour = msToHours(offsetTime); + int offsetMinute = msToMinutes(offsetTime); + + // FIXME: time_t has a potential problem in 2038 + time_t localTime = static_cast<time_t>(localTimeSeconds); + + tm localTM; + getLocalTime(&localTime, &localTM); + + double diff = ((localTM.tm_hour - offsetHour) * secondsPerHour) + ((localTM.tm_min - offsetMinute) * 60); + + if (diff < 0) + diff += secondsPerDay; + + return (diff * msPerSecond); +} + +// Get the DST offset, given a time in UTC +double calculateDSTOffset(double ms, double utcOffset) +{ + // On Mac OS X, the call to localtime (see calculateDSTOffsetSimple) will return historically accurate + // DST information (e.g. New Zealand did not have DST from 1946 to 1974) however the JavaScript + // standard explicitly dictates that historical information should not be considered when + // determining DST. For this reason we shift away from years that localtime can handle but would + // return historically accurate information. + int year = msToYear(ms); + int equivalentYear = equivalentYearForDST(year); + if (year != equivalentYear) { + bool leapYear = isLeapYear(year); + int dayInYearLocal = dayInYear(ms, year); + int dayInMonth = dayInMonthFromDayInYear(dayInYearLocal, leapYear); + int month = monthFromDayInYear(dayInYearLocal, leapYear); + double day = dateToDaysFrom1970(equivalentYear, month, dayInMonth); + ms = (day * msPerDay) + msToMilliseconds(ms); + } + + return calculateDSTOffsetSimple(ms / msPerSecond, utcOffset); +} + +void initializeDates() +{ +#if !ASSERT_DISABLED + static bool alreadyInitialized; + ASSERT(!alreadyInitialized); + alreadyInitialized = true; +#endif + + equivalentYearForDST(2000); // Need to call once to initialize a static used in this function. +} + +static inline double ymdhmsToSeconds(long year, int mon, int day, int hour, int minute, double second) +{ + double days = (day - 32075) + + floor(1461 * (year + 4800.0 + (mon - 14) / 12) / 4) + + 367 * (mon - 2 - (mon - 14) / 12 * 12) / 12 + - floor(3 * ((year + 4900.0 + (mon - 14) / 12) / 100) / 4) + - 2440588; + return ((days * hoursPerDay + hour) * minutesPerHour + minute) * secondsPerMinute + second; +} + +// We follow the recommendation of RFC 2822 to consider all +// obsolete time zones not listed here equivalent to "-0000". +static const struct KnownZone { +#if !OS(WINDOWS) + const +#endif + char tzName[4]; + int tzOffset; +} known_zones[] = { + { "UT", 0 }, + { "GMT", 0 }, + { "EST", -300 }, + { "EDT", -240 }, + { "CST", -360 }, + { "CDT", -300 }, + { "MST", -420 }, + { "MDT", -360 }, + { "PST", -480 }, + { "PDT", -420 } +}; + +inline static void skipSpacesAndComments(const char*& s) +{ + int nesting = 0; + char ch; + while ((ch = *s)) { + if (!isASCIISpace(ch)) { + if (ch == '(') + nesting++; + else if (ch == ')' && nesting > 0) + nesting--; + else if (nesting == 0) + break; + } + s++; + } +} + +// returns 0-11 (Jan-Dec); -1 on failure +static int findMonth(const char* monthStr) +{ + ASSERT(monthStr); + char needle[4]; + for (int i = 0; i < 3; ++i) { + if (!*monthStr) + return -1; + needle[i] = static_cast<char>(toASCIILower(*monthStr++)); + } + needle[3] = '\0'; + const char *haystack = "janfebmaraprmayjunjulaugsepoctnovdec"; + const char *str = strstr(haystack, needle); + if (str) { + int position = static_cast<int>(str - haystack); + if (position % 3 == 0) + return position / 3; + } + return -1; +} + +static bool parseLong(const char* string, char** stopPosition, int base, long* result) +{ + *result = strtol(string, stopPosition, base); + // Avoid the use of errno as it is not available on Windows CE + if (string == *stopPosition || *result == LONG_MIN || *result == LONG_MAX) + return false; + return true; +} + +// Parses a date with the format YYYY[-MM[-DD]]. +// Year parsing is lenient, allows any number of digits, and +/-. +// Returns 0 if a parse error occurs, else returns the end of the parsed portion of the string. +static char* parseES5DatePortion(const char* currentPosition, long& year, long& month, long& day) +{ + char* postParsePosition; + + // This is a bit more lenient on the year string than ES5 specifies: + // instead of restricting to 4 digits (or 6 digits with mandatory +/-), + // it accepts any integer value. Consider this an implementation fallback. + if (!parseLong(currentPosition, &postParsePosition, 10, &year)) + return 0; + + // Check for presence of -MM portion. + if (*postParsePosition != '-') + return postParsePosition; + currentPosition = postParsePosition + 1; + + if (!isASCIIDigit(*currentPosition)) + return 0; + if (!parseLong(currentPosition, &postParsePosition, 10, &month)) + return 0; + if ((postParsePosition - currentPosition) != 2) + return 0; + + // Check for presence of -DD portion. + if (*postParsePosition != '-') + return postParsePosition; + currentPosition = postParsePosition + 1; + + if (!isASCIIDigit(*currentPosition)) + return 0; + if (!parseLong(currentPosition, &postParsePosition, 10, &day)) + return 0; + if ((postParsePosition - currentPosition) != 2) + return 0; + return postParsePosition; +} + +// Parses a time with the format HH:mm[:ss[.sss]][Z|(+|-)00:00]. +// Fractional seconds parsing is lenient, allows any number of digits. +// Returns 0 if a parse error occurs, else returns the end of the parsed portion of the string. +static char* parseES5TimePortion(char* currentPosition, long& hours, long& minutes, double& seconds, long& timeZoneSeconds) +{ + char* postParsePosition; + if (!isASCIIDigit(*currentPosition)) + return 0; + if (!parseLong(currentPosition, &postParsePosition, 10, &hours)) + return 0; + if (*postParsePosition != ':' || (postParsePosition - currentPosition) != 2) + return 0; + currentPosition = postParsePosition + 1; + + if (!isASCIIDigit(*currentPosition)) + return 0; + if (!parseLong(currentPosition, &postParsePosition, 10, &minutes)) + return 0; + if ((postParsePosition - currentPosition) != 2) + return 0; + currentPosition = postParsePosition; + + // Seconds are optional. + if (*currentPosition == ':') { + ++currentPosition; + + long intSeconds; + if (!isASCIIDigit(*currentPosition)) + return 0; + if (!parseLong(currentPosition, &postParsePosition, 10, &intSeconds)) + return 0; + if ((postParsePosition - currentPosition) != 2) + return 0; + seconds = intSeconds; + if (*postParsePosition == '.') { + currentPosition = postParsePosition + 1; + + // In ECMA-262-5 it's a bit unclear if '.' can be present without milliseconds, but + // a reasonable interpretation guided by the given examples and RFC 3339 says "no". + // We check the next character to avoid reading +/- timezone hours after an invalid decimal. + if (!isASCIIDigit(*currentPosition)) + return 0; + + // We are more lenient than ES5 by accepting more or less than 3 fraction digits. + long fracSeconds; + if (!parseLong(currentPosition, &postParsePosition, 10, &fracSeconds)) + return 0; + + long numFracDigits = postParsePosition - currentPosition; + seconds += fracSeconds * pow(10.0, static_cast<double>(-numFracDigits)); + } + currentPosition = postParsePosition; + } + + if (*currentPosition == 'Z') + return currentPosition + 1; + + bool tzNegative; + if (*currentPosition == '-') + tzNegative = true; + else if (*currentPosition == '+') + tzNegative = false; + else + return currentPosition; // no timezone + ++currentPosition; + + long tzHours; + long tzHoursAbs; + long tzMinutes; + + if (!isASCIIDigit(*currentPosition)) + return 0; + if (!parseLong(currentPosition, &postParsePosition, 10, &tzHours)) + return 0; + if (*postParsePosition != ':' || (postParsePosition - currentPosition) != 2) + return 0; + tzHoursAbs = labs(tzHours); + currentPosition = postParsePosition + 1; + + if (!isASCIIDigit(*currentPosition)) + return 0; + if (!parseLong(currentPosition, &postParsePosition, 10, &tzMinutes)) + return 0; + if ((postParsePosition - currentPosition) != 2) + return 0; + currentPosition = postParsePosition; + + if (tzHoursAbs > 24) + return 0; + if (tzMinutes < 0 || tzMinutes > 59) + return 0; + + timeZoneSeconds = 60 * (tzMinutes + (60 * tzHoursAbs)); + if (tzNegative) + timeZoneSeconds = -timeZoneSeconds; + + return currentPosition; +} + +double parseES5DateFromNullTerminatedCharacters(const char* dateString) +{ + // This parses a date of the form defined in ECMA-262-5, section 15.9.1.15 + // (similar to RFC 3339 / ISO 8601: YYYY-MM-DDTHH:mm:ss[.sss]Z). + // In most cases it is intentionally strict (e.g. correct field widths, no stray whitespace). + + static const long daysPerMonth[12] = { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; + + // The year must be present, but the other fields may be omitted - see ES5.1 15.9.1.15. + long year = 0; + long month = 1; + long day = 1; + long hours = 0; + long minutes = 0; + double seconds = 0; + long timeZoneSeconds = 0; + + // Parse the date YYYY[-MM[-DD]] + char* currentPosition = parseES5DatePortion(dateString, year, month, day); + if (!currentPosition) + return std::numeric_limits<double>::quiet_NaN(); + // Look for a time portion. + if (*currentPosition == 'T') { + // Parse the time HH:mm[:ss[.sss]][Z|(+|-)00:00] + currentPosition = parseES5TimePortion(currentPosition + 1, hours, minutes, seconds, timeZoneSeconds); + if (!currentPosition) + return std::numeric_limits<double>::quiet_NaN(); + } + // Check that we have parsed all characters in the string. + if (*currentPosition) + return std::numeric_limits<double>::quiet_NaN(); + + // A few of these checks could be done inline above, but since many of them are interrelated + // we would be sacrificing readability to "optimize" the (presumably less common) failure path. + if (month < 1 || month > 12) + return std::numeric_limits<double>::quiet_NaN(); + if (day < 1 || day > daysPerMonth[month - 1]) + return std::numeric_limits<double>::quiet_NaN(); + if (month == 2 && day > 28 && !isLeapYear(year)) + return std::numeric_limits<double>::quiet_NaN(); + if (hours < 0 || hours > 24) + return std::numeric_limits<double>::quiet_NaN(); + if (hours == 24 && (minutes || seconds)) + return std::numeric_limits<double>::quiet_NaN(); + if (minutes < 0 || minutes > 59) + return std::numeric_limits<double>::quiet_NaN(); + if (seconds < 0 || seconds >= 61) + return std::numeric_limits<double>::quiet_NaN(); + if (seconds > 60) { + // Discard leap seconds by clamping to the end of a minute. + seconds = 60; + } + + double dateSeconds = ymdhmsToSeconds(year, month, day, hours, minutes, seconds) - timeZoneSeconds; + return dateSeconds * msPerSecond; +} + +// Odd case where 'exec' is allowed to be 0, to accomodate a caller in WebCore. +double parseDateFromNullTerminatedCharacters(const char* dateString, bool& haveTZ, int& offset) +{ + haveTZ = false; + offset = 0; + + // This parses a date in the form: + // Tuesday, 09-Nov-99 23:12:40 GMT + // or + // Sat, 01-Jan-2000 08:00:00 GMT + // or + // Sat, 01 Jan 2000 08:00:00 GMT + // or + // 01 Jan 99 22:00 +0100 (exceptions in rfc822/rfc2822) + // ### non RFC formats, added for Javascript: + // [Wednesday] January 09 1999 23:12:40 GMT + // [Wednesday] January 09 23:12:40 GMT 1999 + // + // We ignore the weekday. + + // Skip leading space + skipSpacesAndComments(dateString); + + long month = -1; + const char *wordStart = dateString; + // Check contents of first words if not number + while (*dateString && !isASCIIDigit(*dateString)) { + if (isASCIISpace(*dateString) || *dateString == '(') { + if (dateString - wordStart >= 3) + month = findMonth(wordStart); + skipSpacesAndComments(dateString); + wordStart = dateString; + } else + dateString++; + } + + // Missing delimiter between month and day (like "January29")? + if (month == -1 && wordStart != dateString) + month = findMonth(wordStart); + + skipSpacesAndComments(dateString); + + if (!*dateString) + return std::numeric_limits<double>::quiet_NaN(); + + // ' 09-Nov-99 23:12:40 GMT' + char* newPosStr; + long day; + if (!parseLong(dateString, &newPosStr, 10, &day)) + return std::numeric_limits<double>::quiet_NaN(); + dateString = newPosStr; + + if (!*dateString) + return std::numeric_limits<double>::quiet_NaN(); + + if (day < 0) + return std::numeric_limits<double>::quiet_NaN(); + + long year = 0; + if (day > 31) { + // ### where is the boundary and what happens below? + if (*dateString != '/') + return std::numeric_limits<double>::quiet_NaN(); + // looks like a YYYY/MM/DD date + if (!*++dateString) + return std::numeric_limits<double>::quiet_NaN(); + year = day; + if (!parseLong(dateString, &newPosStr, 10, &month)) + return std::numeric_limits<double>::quiet_NaN(); + month -= 1; + dateString = newPosStr; + if (*dateString++ != '/' || !*dateString) + return std::numeric_limits<double>::quiet_NaN(); + if (!parseLong(dateString, &newPosStr, 10, &day)) + return std::numeric_limits<double>::quiet_NaN(); + dateString = newPosStr; + } else if (*dateString == '/' && month == -1) { + dateString++; + // This looks like a MM/DD/YYYY date, not an RFC date. + month = day - 1; // 0-based + if (!parseLong(dateString, &newPosStr, 10, &day)) + return std::numeric_limits<double>::quiet_NaN(); + if (day < 1 || day > 31) + return std::numeric_limits<double>::quiet_NaN(); + dateString = newPosStr; + if (*dateString == '/') + dateString++; + if (!*dateString) + return std::numeric_limits<double>::quiet_NaN(); + } else { + if (*dateString == '-') + dateString++; + + skipSpacesAndComments(dateString); + + if (*dateString == ',') + dateString++; + + if (month == -1) { // not found yet + month = findMonth(dateString); + if (month == -1) + return std::numeric_limits<double>::quiet_NaN(); + + while (*dateString && *dateString != '-' && *dateString != ',' && !isASCIISpace(*dateString)) + dateString++; + + if (!*dateString) + return std::numeric_limits<double>::quiet_NaN(); + + // '-99 23:12:40 GMT' + if (*dateString != '-' && *dateString != '/' && *dateString != ',' && !isASCIISpace(*dateString)) + return std::numeric_limits<double>::quiet_NaN(); + dateString++; + } + } + + if (month < 0 || month > 11) + return std::numeric_limits<double>::quiet_NaN(); + + // '99 23:12:40 GMT' + if (year <= 0 && *dateString) { + if (!parseLong(dateString, &newPosStr, 10, &year)) + return std::numeric_limits<double>::quiet_NaN(); + } + + // Don't fail if the time is missing. + long hour = 0; + long minute = 0; + long second = 0; + if (!*newPosStr) + dateString = newPosStr; + else { + // ' 23:12:40 GMT' + if (!(isASCIISpace(*newPosStr) || *newPosStr == ',')) { + if (*newPosStr != ':') + return std::numeric_limits<double>::quiet_NaN(); + // There was no year; the number was the hour. + year = -1; + } else { + // in the normal case (we parsed the year), advance to the next number + dateString = ++newPosStr; + skipSpacesAndComments(dateString); + } + + parseLong(dateString, &newPosStr, 10, &hour); + // Do not check for errno here since we want to continue + // even if errno was set becasue we are still looking + // for the timezone! + + // Read a number? If not, this might be a timezone name. + if (newPosStr != dateString) { + dateString = newPosStr; + + if (hour < 0 || hour > 23) + return std::numeric_limits<double>::quiet_NaN(); + + if (!*dateString) + return std::numeric_limits<double>::quiet_NaN(); + + // ':12:40 GMT' + if (*dateString++ != ':') + return std::numeric_limits<double>::quiet_NaN(); + + if (!parseLong(dateString, &newPosStr, 10, &minute)) + return std::numeric_limits<double>::quiet_NaN(); + dateString = newPosStr; + + if (minute < 0 || minute > 59) + return std::numeric_limits<double>::quiet_NaN(); + + // ':40 GMT' + if (*dateString && *dateString != ':' && !isASCIISpace(*dateString)) + return std::numeric_limits<double>::quiet_NaN(); + + // seconds are optional in rfc822 + rfc2822 + if (*dateString ==':') { + dateString++; + + if (!parseLong(dateString, &newPosStr, 10, &second)) + return std::numeric_limits<double>::quiet_NaN(); + dateString = newPosStr; + + if (second < 0 || second > 59) + return std::numeric_limits<double>::quiet_NaN(); + } + + skipSpacesAndComments(dateString); + + if (strncasecmp(dateString, "AM", 2) == 0) { + if (hour > 12) + return std::numeric_limits<double>::quiet_NaN(); + if (hour == 12) + hour = 0; + dateString += 2; + skipSpacesAndComments(dateString); + } else if (strncasecmp(dateString, "PM", 2) == 0) { + if (hour > 12) + return std::numeric_limits<double>::quiet_NaN(); + if (hour != 12) + hour += 12; + dateString += 2; + skipSpacesAndComments(dateString); + } + } + } + + // Don't fail if the time zone is missing. + // Some websites omit the time zone (4275206). + if (*dateString) { + if (strncasecmp(dateString, "GMT", 3) == 0 || strncasecmp(dateString, "UTC", 3) == 0) { + dateString += 3; + haveTZ = true; + } + + if (*dateString == '+' || *dateString == '-') { + long o; + if (!parseLong(dateString, &newPosStr, 10, &o)) + return std::numeric_limits<double>::quiet_NaN(); + dateString = newPosStr; + + if (o < -9959 || o > 9959) + return std::numeric_limits<double>::quiet_NaN(); + + int sgn = (o < 0) ? -1 : 1; + o = labs(o); + if (*dateString != ':') { + if (o >= 24) + offset = ((o / 100) * 60 + (o % 100)) * sgn; + else + offset = o * 60 * sgn; + } else { // GMT+05:00 + long o2; + if (!parseLong(dateString, &newPosStr, 10, &o2)) + return std::numeric_limits<double>::quiet_NaN(); + dateString = newPosStr; + offset = (o * 60 + o2) * sgn; + } + haveTZ = true; + } else { + for (size_t i = 0; i < WTF_ARRAY_LENGTH(known_zones); ++i) { + if (0 == strncasecmp(dateString, known_zones[i].tzName, strlen(known_zones[i].tzName))) { + offset = known_zones[i].tzOffset; + dateString += strlen(known_zones[i].tzName); + haveTZ = true; + break; + } + } + } + } + + skipSpacesAndComments(dateString); + + if (*dateString && year == -1) { + if (!parseLong(dateString, &newPosStr, 10, &year)) + return std::numeric_limits<double>::quiet_NaN(); + dateString = newPosStr; + } + + skipSpacesAndComments(dateString); + + // Trailing garbage + if (*dateString) + return std::numeric_limits<double>::quiet_NaN(); + + // Y2K: Handle 2 digit years. + if (year >= 0 && year < 100) { + if (year < 50) + year += 2000; + else + year += 1900; + } + + return ymdhmsToSeconds(year, month + 1, day, hour, minute, second) * msPerSecond; +} + +double parseDateFromNullTerminatedCharacters(const char* dateString) +{ + bool haveTZ; + int offset; + double ms = parseDateFromNullTerminatedCharacters(dateString, haveTZ, offset); + if (isnan(ms)) + return std::numeric_limits<double>::quiet_NaN(); + + // fall back to local timezone + if (!haveTZ) { + double utcOffset = calculateUTCOffset(); + double dstOffset = calculateDSTOffset(ms, utcOffset); + offset = static_cast<int>((utcOffset + dstOffset) / msPerMinute); + } + return ms - (offset * msPerMinute); +} + +double timeClip(double t) +{ + if (!isfinite(t)) + return std::numeric_limits<double>::quiet_NaN(); + if (fabs(t) > maxECMAScriptTime) + return std::numeric_limits<double>::quiet_NaN(); + return trunc(t); +} + +// See http://tools.ietf.org/html/rfc2822#section-3.3 for more information. +String makeRFC2822DateString(unsigned dayOfWeek, unsigned day, unsigned month, unsigned year, unsigned hours, unsigned minutes, unsigned seconds, int utcOffset) +{ + StringBuilder stringBuilder; + stringBuilder.append(weekdayName[dayOfWeek]); + stringBuilder.append(", "); + stringBuilder.append(String::number(day)); + stringBuilder.append(" "); + stringBuilder.append(monthName[month]); + stringBuilder.append(" "); + stringBuilder.append(String::number(year)); + stringBuilder.append(" "); + + stringBuilder.append(twoDigitStringFromNumber(hours)); + stringBuilder.append(':'); + stringBuilder.append(twoDigitStringFromNumber(minutes)); + stringBuilder.append(':'); + stringBuilder.append(twoDigitStringFromNumber(seconds)); + stringBuilder.append(' '); + + stringBuilder.append(utcOffset > 0 ? "+" : "-"); + int absoluteUTCOffset = abs(utcOffset); + stringBuilder.append(twoDigitStringFromNumber(absoluteUTCOffset / 60)); + stringBuilder.append(twoDigitStringFromNumber(absoluteUTCOffset % 60)); + + return stringBuilder.toString(); +} + +} // namespace WTF diff --git a/Source/JavaScriptCore/wtf/DateMath.h b/Source/JavaScriptCore/wtf/DateMath.h new file mode 100644 index 000000000..114acf8a8 --- /dev/null +++ b/Source/JavaScriptCore/wtf/DateMath.h @@ -0,0 +1,126 @@ +/* + * Copyright (C) 1999-2000 Harri Porten (porten@kde.org) + * Copyright (C) 2006, 2007 Apple Inc. All rights reserved. + * Copyright (C) 2009 Google Inc. All rights reserved. + * Copyright (C) 2010 Research In Motion Limited. All rights reserved. + * + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (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.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + */ + +#ifndef DateMath_h +#define DateMath_h + +#include <math.h> +#include <stdint.h> +#include <string.h> +#include <time.h> +#include <wtf/CurrentTime.h> +#include <wtf/Noncopyable.h> +#include <wtf/OwnArrayPtr.h> +#include <wtf/PassOwnArrayPtr.h> +#include <wtf/text/WTFString.h> +#include <wtf/UnusedParam.h> + +namespace WTF { + +void initializeDates(); +int equivalentYearForDST(int year); + +// Not really math related, but this is currently the only shared place to put these. +double parseES5DateFromNullTerminatedCharacters(const char* dateString); +double parseDateFromNullTerminatedCharacters(const char* dateString); +double parseDateFromNullTerminatedCharacters(const char* dateString, bool& haveTZ, int& offset); +double timeClip(double); +// dayOfWeek: [0, 6] 0 being Monday, day: [1, 31], month: [0, 11], year: ex: 2011, hours: [0, 23], minutes: [0, 59], seconds: [0, 59], utcOffset: [-720,720]. +String makeRFC2822DateString(unsigned dayOfWeek, unsigned day, unsigned month, unsigned year, unsigned hours, unsigned minutes, unsigned seconds, int utcOffset); + +inline double jsCurrentTime() +{ + // JavaScript doesn't recognize fractions of a millisecond. + return floor(WTF::currentTimeMS()); +} + +const char* const weekdayName[7] = { "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun" }; +const char* const monthName[12] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; + +const double hoursPerDay = 24.0; +const double minutesPerHour = 60.0; +const double secondsPerHour = 60.0 * 60.0; +const double secondsPerMinute = 60.0; +const double msPerSecond = 1000.0; +const double msPerMinute = 60.0 * 1000.0; +const double msPerHour = 60.0 * 60.0 * 1000.0; +const double msPerDay = 24.0 * 60.0 * 60.0 * 1000.0; +const double msPerMonth = 2592000000.0; + +bool isLeapYear(int year); + +// Returns the number of days from 1970-01-01 to the specified date. +double dateToDaysFrom1970(int year, int month, int day); +int msToYear(double ms); +double msToDays(double ms); +int msToMinutes(double ms); +int msToHours(double ms); +int dayInYear(double ms, int year); +int monthFromDayInYear(int dayInYear, bool leapYear); +int dayInMonthFromDayInYear(int dayInYear, bool leapYear); + +// Returns offset milliseconds for UTC and DST. +int32_t calculateUTCOffset(); +double calculateDSTOffset(double ms, double utcOffset); + +} // namespace WTF + +using WTF::isLeapYear; +using WTF::dateToDaysFrom1970; +using WTF::dayInMonthFromDayInYear; +using WTF::dayInYear; +using WTF::minutesPerHour; +using WTF::monthFromDayInYear; +using WTF::msPerDay; +using WTF::msPerMinute; +using WTF::msPerSecond; +using WTF::msToYear; +using WTF::msToDays; +using WTF::msToMinutes; +using WTF::msToHours; +using WTF::secondsPerMinute; +using WTF::parseDateFromNullTerminatedCharacters; +using WTF::makeRFC2822DateString; +using WTF::calculateUTCOffset; +using WTF::calculateDSTOffset; + +#endif // DateMath_h diff --git a/Source/JavaScriptCore/wtf/DecimalNumber.cpp b/Source/JavaScriptCore/wtf/DecimalNumber.cpp new file mode 100644 index 000000000..70304e2e5 --- /dev/null +++ b/Source/JavaScriptCore/wtf/DecimalNumber.cpp @@ -0,0 +1,199 @@ +/* + * Copyright (C) 2010 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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 APPLE INC. ``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 APPLE INC. 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 "config.h" +#include "DecimalNumber.h" +#include <math.h> +#include <wtf/MathExtras.h> +#include <wtf/text/WTFString.h> + +namespace WTF { + +unsigned DecimalNumber::bufferLengthForStringDecimal() const +{ + unsigned length = 0; + // if the exponent is negative the number decimal representation is of the form: + // [<sign>]0.[<zeros>]<significand> + if (m_exponent < 0) { + if (m_sign) + ++length; + length += 2; // for "0." + length += -m_exponent - 1; + length += m_precision; + return length; + } + + unsigned digitsBeforeDecimalPoint = m_exponent + 1; + + // If the precision is <= than the number of digits to get up to the decimal + // point, then there is no fractional part, number is of the form: + // [<sign>]<significand>[<zeros>] + if (m_precision <= digitsBeforeDecimalPoint) { + if (m_sign) + ++length; + length += m_precision; + length += digitsBeforeDecimalPoint - m_precision; + return length; + } + + // If we get here, number starts before the decimal point, and ends after it, + // as such is of the form: + // [<sign>]<significand-begin>.<significand-end> + if (m_sign) + ++length; + length += digitsBeforeDecimalPoint; + ++length; // for decimal point + length += m_precision - digitsBeforeDecimalPoint; + + return length; +} + +unsigned DecimalNumber::bufferLengthForStringExponential() const +{ + unsigned length = 0; + if (m_sign) + ++length; + + // Add the significand + ++length; + + if (m_precision > 1) { + ++length; // for decimal point + length += m_precision - 1; + } + + // Add "e+" or "e-" + length += 2; + + int exponent = (m_exponent >= 0) ? m_exponent : -m_exponent; + + // Add the exponent + if (exponent >= 100) + ++length; + if (exponent >= 10) + ++length; + ++length; + + return length; +} + +unsigned DecimalNumber::toStringDecimal(UChar* buffer, unsigned bufferLength) const +{ + ASSERT_UNUSED(bufferLength, bufferLength >= bufferLengthForStringDecimal()); + + // Should always be at least one digit to add to the string! + ASSERT(m_precision); + UChar* next = buffer; + + // if the exponent is negative the number decimal representation is of the form: + // [<sign>]0.[<zeros>]<significand> + if (m_exponent < 0) { + unsigned zeros = -m_exponent - 1; + + if (m_sign) + *next++ = '-'; + *next++ = '0'; + *next++ = '.'; + for (unsigned i = 0; i < zeros; ++i) + *next++ = '0'; + for (unsigned i = 0; i < m_precision; ++i) + *next++ = m_significand[i]; + + return next - buffer; + } + + unsigned digitsBeforeDecimalPoint = m_exponent + 1; + + // If the precision is <= than the number of digits to get up to the decimal + // point, then there is no fractional part, number is of the form: + // [<sign>]<significand>[<zeros>] + if (m_precision <= digitsBeforeDecimalPoint) { + if (m_sign) + *next++ = '-'; + for (unsigned i = 0; i < m_precision; ++i) + *next++ = m_significand[i]; + for (unsigned i = 0; i < (digitsBeforeDecimalPoint - m_precision); ++i) + *next++ = '0'; + + return next - buffer; + } + + // If we get here, number starts before the decimal point, and ends after it, + // as such is of the form: + // [<sign>]<significand-begin>.<significand-end> + + if (m_sign) + *next++ = '-'; + for (unsigned i = 0; i < digitsBeforeDecimalPoint; ++i) + *next++ = m_significand[i]; + *next++ = '.'; + for (unsigned i = digitsBeforeDecimalPoint; i < m_precision; ++i) + *next++ = m_significand[i]; + + return next - buffer; +} + +unsigned DecimalNumber::toStringExponential(UChar* buffer, unsigned bufferLength) const +{ + ASSERT_UNUSED(bufferLength, bufferLength >= bufferLengthForStringExponential()); + + // Should always be at least one digit to add to the string! + ASSERT(m_precision); + UChar* next = buffer; + + // Add the sign + if (m_sign) + *next++ = '-'; + + // Add the significand + *next++ = m_significand[0]; + if (m_precision > 1) { + *next++ = '.'; + for (unsigned i = 1; i < m_precision; ++i) + *next++ = m_significand[i]; + } + + // Add "e+" or "e-" + *next++ = 'e'; + int exponent; + if (m_exponent >= 0) { + *next++ = '+'; + exponent = m_exponent; + } else { + *next++ = '-'; + exponent = -m_exponent; + } + + // Add the exponent + if (exponent >= 100) + *next++ = '0' + exponent / 100; + if (exponent >= 10) + *next++ = '0' + (exponent % 100) / 10; + *next++ = '0' + exponent % 10; + + return next - buffer; +} + +} // namespace WTF diff --git a/Source/JavaScriptCore/wtf/DecimalNumber.h b/Source/JavaScriptCore/wtf/DecimalNumber.h new file mode 100644 index 000000000..ac7b80058 --- /dev/null +++ b/Source/JavaScriptCore/wtf/DecimalNumber.h @@ -0,0 +1,108 @@ +/* + * Copyright (C) 2010 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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 APPLE INC. ``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 APPLE INC. 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. + */ + +#ifndef DecimalNumber_h +#define DecimalNumber_h + +#include <math.h> +#include <wtf/dtoa.h> +#include <wtf/MathExtras.h> +#include <wtf/text/WTFString.h> + +namespace WTF { + +enum RoundingSignificantFiguresType { RoundingSignificantFigures }; +enum RoundingDecimalPlacesType { RoundingDecimalPlaces }; + +class DecimalNumber { +public: + DecimalNumber(double d) + { + ASSERT(isfinite(d)); + dtoa(m_significand, d, m_sign, m_exponent, m_precision); + + ASSERT(m_precision); + // Zero should always have exponent 0. + ASSERT(m_significand[0] != '0' || !m_exponent); + // No values other than zero should have a leading zero. + ASSERT(m_significand[0] != '0' || m_precision == 1); + // No values other than zero should have trailing zeros. + ASSERT(m_significand[0] == '0' || m_significand[m_precision - 1] != '0'); + } + + DecimalNumber(double d, RoundingSignificantFiguresType, unsigned significantFigures) + { + ASSERT(isfinite(d)); + dtoaRoundSF(m_significand, d, significantFigures, m_sign, m_exponent, m_precision); + + ASSERT(significantFigures && significantFigures <= sizeof(DtoaBuffer)); + while (m_precision < significantFigures) + m_significand[m_precision++] = '0'; + + ASSERT(m_precision); + // Zero should always have exponent 0. + ASSERT(m_significand[0] != '0' || !m_exponent); + } + + DecimalNumber(double d, RoundingDecimalPlacesType, unsigned decimalPlaces) + { + ASSERT(isfinite(d)); + dtoaRoundDP(m_significand, d, decimalPlaces, m_sign, m_exponent, m_precision); + + unsigned significantFigures = 1 + m_exponent + decimalPlaces; + ASSERT(significantFigures && significantFigures <= sizeof(DtoaBuffer)); + while (m_precision < significantFigures) + m_significand[m_precision++] = '0'; + + ASSERT(m_precision); + // Zero should always have exponent 0. + ASSERT(m_significand[0] != '0' || !m_exponent); + } + + unsigned bufferLengthForStringDecimal() const; + unsigned bufferLengthForStringExponential() const; + + unsigned toStringDecimal(UChar* buffer, unsigned bufferLength) const; + unsigned toStringExponential(UChar* buffer, unsigned bufferLength) const; + + bool sign() const { return m_sign; } + int exponent() const { return m_exponent; } + const char* significand() const { return m_significand; } // significand contains precision characters, is not null-terminated. + unsigned precision() const { return m_precision; } + +private: + bool m_sign; + int m_exponent; + DtoaBuffer m_significand; + unsigned m_precision; +}; + +} // namespace WTF + +using WTF::DecimalNumber; +using WTF::RoundingSignificantFigures; +using WTF::RoundingDecimalPlaces; + +#endif // DecimalNumber_h diff --git a/Source/JavaScriptCore/wtf/Decoder.h b/Source/JavaScriptCore/wtf/Decoder.h new file mode 100644 index 000000000..341d58d73 --- /dev/null +++ b/Source/JavaScriptCore/wtf/Decoder.h @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2011 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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 APPLE INC. ``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 APPLE INC. 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. + */ + +#ifndef Decoder_h +#define Decoder_h + +#include <wtf/Vector.h> + +namespace WTF { + +class String; + +class Decoder { +protected: + Decoder() { } + virtual ~Decoder() { } + +public: + virtual bool decodeBytes(Vector<uint8_t>&) = 0; + + virtual bool decodeBool(bool&) = 0; + virtual bool decodeUInt32(uint32_t&) = 0; + virtual bool decodeUInt64(uint64_t&) = 0; + virtual bool decodeInt32(int32_t&) = 0; + virtual bool decodeInt64(int64_t&) = 0; + virtual bool decodeFloat(float&) = 0; + virtual bool decodeDouble(double&) = 0; + virtual bool decodeString(String&) = 0; +}; + +} // namespace WTF + +using WTF::Decoder; + +#endif // Decoder_h diff --git a/Source/JavaScriptCore/wtf/Deque.h b/Source/JavaScriptCore/wtf/Deque.h new file mode 100644 index 000000000..18eb10582 --- /dev/null +++ b/Source/JavaScriptCore/wtf/Deque.h @@ -0,0 +1,690 @@ +/* + * Copyright (C) 2007, 2008 Apple Inc. All rights reserved. + * Copyright (C) 2009 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of Apple Computer, Inc. ("Apple") 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 APPLE AND ITS 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 APPLE OR ITS 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. + */ + +#ifndef WTF_Deque_h +#define WTF_Deque_h + +// FIXME: Could move what Vector and Deque share into a separate file. +// Deque doesn't actually use Vector. + +#include "PassTraits.h" +#include "Vector.h" + +namespace WTF { + + template<typename T, size_t inlineCapacity> class DequeIteratorBase; + template<typename T, size_t inlineCapacity> class DequeIterator; + template<typename T, size_t inlineCapacity> class DequeConstIterator; + template<typename T, size_t inlineCapacity> class DequeReverseIterator; + template<typename T, size_t inlineCapacity> class DequeConstReverseIterator; + + template<typename T, size_t inlineCapacity = 0> + class Deque { + WTF_MAKE_FAST_ALLOCATED; + public: + typedef DequeIterator<T, inlineCapacity> iterator; + typedef DequeConstIterator<T, inlineCapacity> const_iterator; + typedef DequeReverseIterator<T, inlineCapacity> reverse_iterator; + typedef DequeConstReverseIterator<T, inlineCapacity> const_reverse_iterator; + typedef PassTraits<T> Pass; + typedef typename PassTraits<T>::PassType PassType; + + Deque(); + Deque(const Deque<T, inlineCapacity>&); + Deque& operator=(const Deque<T, inlineCapacity>&); + ~Deque(); + + void swap(Deque<T, inlineCapacity>&); + + size_t size() const { return m_start <= m_end ? m_end - m_start : m_end + m_buffer.capacity() - m_start; } + bool isEmpty() const { return m_start == m_end; } + + iterator begin() { return iterator(this, m_start); } + iterator end() { return iterator(this, m_end); } + const_iterator begin() const { return const_iterator(this, m_start); } + const_iterator end() const { return const_iterator(this, m_end); } + reverse_iterator rbegin() { return reverse_iterator(this, m_end); } + reverse_iterator rend() { return reverse_iterator(this, m_start); } + const_reverse_iterator rbegin() const { return const_reverse_iterator(this, m_end); } + const_reverse_iterator rend() const { return const_reverse_iterator(this, m_start); } + + T& first() { ASSERT(m_start != m_end); return m_buffer.buffer()[m_start]; } + const T& first() const { ASSERT(m_start != m_end); return m_buffer.buffer()[m_start]; } + PassType takeFirst(); + + T& last() { ASSERT(m_start != m_end); return *(--end()); } + const T& last() const { ASSERT(m_start != m_end); return *(--end()); } + + template<typename U> void append(const U&); + template<typename U> void prepend(const U&); + void removeFirst(); + void remove(iterator&); + void remove(const_iterator&); + + void clear(); + + template<typename Predicate> + iterator findIf(Predicate&); + + private: + friend class DequeIteratorBase<T, inlineCapacity>; + + typedef VectorBuffer<T, inlineCapacity> Buffer; + typedef VectorTypeOperations<T> TypeOperations; + typedef DequeIteratorBase<T, inlineCapacity> IteratorBase; + + void remove(size_t position); + void invalidateIterators(); + void destroyAll(); + void checkValidity() const; + void checkIndexValidity(size_t) const; + void expandCapacityIfNeeded(); + void expandCapacity(); + + size_t m_start; + size_t m_end; + Buffer m_buffer; +#ifndef NDEBUG + mutable IteratorBase* m_iterators; +#endif + }; + + template<typename T, size_t inlineCapacity = 0> + class DequeIteratorBase { + private: + typedef DequeIteratorBase<T, inlineCapacity> Base; + + protected: + DequeIteratorBase(); + DequeIteratorBase(const Deque<T, inlineCapacity>*, size_t); + DequeIteratorBase(const Base&); + Base& operator=(const Base&); + ~DequeIteratorBase(); + + void assign(const Base& other) { *this = other; } + + void increment(); + void decrement(); + + T* before() const; + T* after() const; + + bool isEqual(const Base&) const; + + private: + void addToIteratorsList(); + void removeFromIteratorsList(); + void checkValidity() const; + void checkValidity(const Base&) const; + + Deque<T, inlineCapacity>* m_deque; + size_t m_index; + + friend class Deque<T, inlineCapacity>; + +#ifndef NDEBUG + mutable DequeIteratorBase* m_next; + mutable DequeIteratorBase* m_previous; +#endif + }; + + template<typename T, size_t inlineCapacity = 0> + class DequeIterator : public DequeIteratorBase<T, inlineCapacity> { + private: + typedef DequeIteratorBase<T, inlineCapacity> Base; + typedef DequeIterator<T, inlineCapacity> Iterator; + + public: + DequeIterator(Deque<T, inlineCapacity>* deque, size_t index) : Base(deque, index) { } + + DequeIterator(const Iterator& other) : Base(other) { } + DequeIterator& operator=(const Iterator& other) { Base::assign(other); return *this; } + + T& operator*() const { return *Base::after(); } + T* operator->() const { return Base::after(); } + + bool operator==(const Iterator& other) const { return Base::isEqual(other); } + bool operator!=(const Iterator& other) const { return !Base::isEqual(other); } + + Iterator& operator++() { Base::increment(); return *this; } + // postfix ++ intentionally omitted + Iterator& operator--() { Base::decrement(); return *this; } + // postfix -- intentionally omitted + }; + + template<typename T, size_t inlineCapacity = 0> + class DequeConstIterator : public DequeIteratorBase<T, inlineCapacity> { + private: + typedef DequeIteratorBase<T, inlineCapacity> Base; + typedef DequeConstIterator<T, inlineCapacity> Iterator; + typedef DequeIterator<T, inlineCapacity> NonConstIterator; + + public: + DequeConstIterator(const Deque<T, inlineCapacity>* deque, size_t index) : Base(deque, index) { } + + DequeConstIterator(const Iterator& other) : Base(other) { } + DequeConstIterator(const NonConstIterator& other) : Base(other) { } + DequeConstIterator& operator=(const Iterator& other) { Base::assign(other); return *this; } + DequeConstIterator& operator=(const NonConstIterator& other) { Base::assign(other); return *this; } + + const T& operator*() const { return *Base::after(); } + const T* operator->() const { return Base::after(); } + + bool operator==(const Iterator& other) const { return Base::isEqual(other); } + bool operator!=(const Iterator& other) const { return !Base::isEqual(other); } + + Iterator& operator++() { Base::increment(); return *this; } + // postfix ++ intentionally omitted + Iterator& operator--() { Base::decrement(); return *this; } + // postfix -- intentionally omitted + }; + + template<typename T, size_t inlineCapacity = 0> + class DequeReverseIterator : public DequeIteratorBase<T, inlineCapacity> { + private: + typedef DequeIteratorBase<T, inlineCapacity> Base; + typedef DequeReverseIterator<T, inlineCapacity> Iterator; + + public: + DequeReverseIterator(const Deque<T, inlineCapacity>* deque, size_t index) : Base(deque, index) { } + + DequeReverseIterator(const Iterator& other) : Base(other) { } + DequeReverseIterator& operator=(const Iterator& other) { Base::assign(other); return *this; } + + T& operator*() const { return *Base::before(); } + T* operator->() const { return Base::before(); } + + bool operator==(const Iterator& other) const { return Base::isEqual(other); } + bool operator!=(const Iterator& other) const { return !Base::isEqual(other); } + + Iterator& operator++() { Base::decrement(); return *this; } + // postfix ++ intentionally omitted + Iterator& operator--() { Base::increment(); return *this; } + // postfix -- intentionally omitted + }; + + template<typename T, size_t inlineCapacity = 0> + class DequeConstReverseIterator : public DequeIteratorBase<T, inlineCapacity> { + private: + typedef DequeIteratorBase<T, inlineCapacity> Base; + typedef DequeConstReverseIterator<T, inlineCapacity> Iterator; + typedef DequeReverseIterator<T, inlineCapacity> NonConstIterator; + + public: + DequeConstReverseIterator(const Deque<T, inlineCapacity>* deque, size_t index) : Base(deque, index) { } + + DequeConstReverseIterator(const Iterator& other) : Base(other) { } + DequeConstReverseIterator(const NonConstIterator& other) : Base(other) { } + DequeConstReverseIterator& operator=(const Iterator& other) { Base::assign(other); return *this; } + DequeConstReverseIterator& operator=(const NonConstIterator& other) { Base::assign(other); return *this; } + + const T& operator*() const { return *Base::before(); } + const T* operator->() const { return Base::before(); } + + bool operator==(const Iterator& other) const { return Base::isEqual(other); } + bool operator!=(const Iterator& other) const { return !Base::isEqual(other); } + + Iterator& operator++() { Base::decrement(); return *this; } + // postfix ++ intentionally omitted + Iterator& operator--() { Base::increment(); return *this; } + // postfix -- intentionally omitted + }; + +#ifdef NDEBUG + template<typename T, size_t inlineCapacity> inline void Deque<T, inlineCapacity>::checkValidity() const { } + template<typename T, size_t inlineCapacity> inline void Deque<T, inlineCapacity>::checkIndexValidity(size_t) const { } + template<typename T, size_t inlineCapacity> inline void Deque<T, inlineCapacity>::invalidateIterators() { } +#else + template<typename T, size_t inlineCapacity> + void Deque<T, inlineCapacity>::checkValidity() const + { + // In this implementation a capacity of 1 would confuse append() and + // other places that assume the index after capacity - 1 is 0. + ASSERT(m_buffer.capacity() != 1); + + if (!m_buffer.capacity()) { + ASSERT(!m_start); + ASSERT(!m_end); + } else { + ASSERT(m_start < m_buffer.capacity()); + ASSERT(m_end < m_buffer.capacity()); + } + } + + template<typename T, size_t inlineCapacity> + void Deque<T, inlineCapacity>::checkIndexValidity(size_t index) const + { + ASSERT_UNUSED(index, index <= m_buffer.capacity()); + if (m_start <= m_end) { + ASSERT(index >= m_start); + ASSERT(index <= m_end); + } else { + ASSERT(index >= m_start || index <= m_end); + } + } + + template<typename T, size_t inlineCapacity> + void Deque<T, inlineCapacity>::invalidateIterators() + { + IteratorBase* next; + for (IteratorBase* p = m_iterators; p; p = next) { + next = p->m_next; + p->m_deque = 0; + p->m_next = 0; + p->m_previous = 0; + } + m_iterators = 0; + } +#endif + + template<typename T, size_t inlineCapacity> + inline Deque<T, inlineCapacity>::Deque() + : m_start(0) + , m_end(0) +#ifndef NDEBUG + , m_iterators(0) +#endif + { + checkValidity(); + } + + template<typename T, size_t inlineCapacity> + inline Deque<T, inlineCapacity>::Deque(const Deque<T, inlineCapacity>& other) + : m_start(other.m_start) + , m_end(other.m_end) + , m_buffer(other.m_buffer.capacity()) +#ifndef NDEBUG + , m_iterators(0) +#endif + { + const T* otherBuffer = other.m_buffer.buffer(); + if (m_start <= m_end) + TypeOperations::uninitializedCopy(otherBuffer + m_start, otherBuffer + m_end, m_buffer.buffer() + m_start); + else { + TypeOperations::uninitializedCopy(otherBuffer, otherBuffer + m_end, m_buffer.buffer()); + TypeOperations::uninitializedCopy(otherBuffer + m_start, otherBuffer + m_buffer.capacity(), m_buffer.buffer() + m_start); + } + } + + template<typename T, size_t inlineCapacity> + void deleteAllValues(const Deque<T, inlineCapacity>& collection) + { + typedef typename Deque<T, inlineCapacity>::const_iterator iterator; + iterator end = collection.end(); + for (iterator it = collection.begin(); it != end; ++it) + delete *it; + } + + template<typename T, size_t inlineCapacity> + inline Deque<T, inlineCapacity>& Deque<T, inlineCapacity>::operator=(const Deque<T, inlineCapacity>& other) + { + // FIXME: This is inefficient if we're using an inline buffer and T is + // expensive to copy since it will copy the buffer twice instead of once. + Deque<T> copy(other); + swap(copy); + return *this; + } + + template<typename T, size_t inlineCapacity> + inline void Deque<T, inlineCapacity>::destroyAll() + { + if (m_start <= m_end) + TypeOperations::destruct(m_buffer.buffer() + m_start, m_buffer.buffer() + m_end); + else { + TypeOperations::destruct(m_buffer.buffer(), m_buffer.buffer() + m_end); + TypeOperations::destruct(m_buffer.buffer() + m_start, m_buffer.buffer() + m_buffer.capacity()); + } + } + + template<typename T, size_t inlineCapacity> + inline Deque<T, inlineCapacity>::~Deque() + { + checkValidity(); + invalidateIterators(); + destroyAll(); + } + + template<typename T, size_t inlineCapacity> + inline void Deque<T, inlineCapacity>::swap(Deque<T, inlineCapacity>& other) + { + checkValidity(); + other.checkValidity(); + invalidateIterators(); + std::swap(m_start, other.m_start); + std::swap(m_end, other.m_end); + m_buffer.swap(other.m_buffer); + checkValidity(); + other.checkValidity(); + } + + template<typename T, size_t inlineCapacity> + inline void Deque<T, inlineCapacity>::clear() + { + checkValidity(); + invalidateIterators(); + destroyAll(); + m_start = 0; + m_end = 0; + checkValidity(); + } + + template<typename T, size_t inlineCapacity> + template<typename Predicate> + inline DequeIterator<T, inlineCapacity> Deque<T, inlineCapacity>::findIf(Predicate& predicate) + { + iterator end_iterator = end(); + for (iterator it = begin(); it != end_iterator; ++it) { + if (predicate(*it)) + return it; + } + return end_iterator; + } + + template<typename T, size_t inlineCapacity> + inline void Deque<T, inlineCapacity>::expandCapacityIfNeeded() + { + if (m_start) { + if (m_end + 1 != m_start) + return; + } else if (m_end) { + if (m_end != m_buffer.capacity() - 1) + return; + } else if (m_buffer.capacity()) + return; + + expandCapacity(); + } + + template<typename T, size_t inlineCapacity> + void Deque<T, inlineCapacity>::expandCapacity() + { + checkValidity(); + size_t oldCapacity = m_buffer.capacity(); + size_t newCapacity = max(static_cast<size_t>(16), oldCapacity + oldCapacity / 4 + 1); + T* oldBuffer = m_buffer.buffer(); + m_buffer.allocateBuffer(newCapacity); + if (m_start <= m_end) + TypeOperations::move(oldBuffer + m_start, oldBuffer + m_end, m_buffer.buffer() + m_start); + else { + TypeOperations::move(oldBuffer, oldBuffer + m_end, m_buffer.buffer()); + size_t newStart = newCapacity - (oldCapacity - m_start); + TypeOperations::move(oldBuffer + m_start, oldBuffer + oldCapacity, m_buffer.buffer() + newStart); + m_start = newStart; + } + m_buffer.deallocateBuffer(oldBuffer); + checkValidity(); + } + + template<typename T, size_t inlineCapacity> + inline typename Deque<T, inlineCapacity>::PassType Deque<T, inlineCapacity>::takeFirst() + { + T oldFirst = Pass::transfer(first()); + removeFirst(); + return Pass::transfer(oldFirst); + } + + template<typename T, size_t inlineCapacity> template<typename U> + inline void Deque<T, inlineCapacity>::append(const U& value) + { + checkValidity(); + expandCapacityIfNeeded(); + new (NotNull, &m_buffer.buffer()[m_end]) T(value); + if (m_end == m_buffer.capacity() - 1) + m_end = 0; + else + ++m_end; + checkValidity(); + } + + template<typename T, size_t inlineCapacity> template<typename U> + inline void Deque<T, inlineCapacity>::prepend(const U& value) + { + checkValidity(); + expandCapacityIfNeeded(); + if (!m_start) + m_start = m_buffer.capacity() - 1; + else + --m_start; + new (NotNull, &m_buffer.buffer()[m_start]) T(value); + checkValidity(); + } + + template<typename T, size_t inlineCapacity> + inline void Deque<T, inlineCapacity>::removeFirst() + { + checkValidity(); + invalidateIterators(); + ASSERT(!isEmpty()); + TypeOperations::destruct(&m_buffer.buffer()[m_start], &m_buffer.buffer()[m_start + 1]); + if (m_start == m_buffer.capacity() - 1) + m_start = 0; + else + ++m_start; + checkValidity(); + } + + template<typename T, size_t inlineCapacity> + inline void Deque<T, inlineCapacity>::remove(iterator& it) + { + it.checkValidity(); + remove(it.m_index); + } + + template<typename T, size_t inlineCapacity> + inline void Deque<T, inlineCapacity>::remove(const_iterator& it) + { + it.checkValidity(); + remove(it.m_index); + } + + template<typename T, size_t inlineCapacity> + inline void Deque<T, inlineCapacity>::remove(size_t position) + { + if (position == m_end) + return; + + checkValidity(); + invalidateIterators(); + + T* buffer = m_buffer.buffer(); + TypeOperations::destruct(&buffer[position], &buffer[position + 1]); + + // Find which segment of the circular buffer contained the remove element, and only move elements in that part. + if (position >= m_start) { + TypeOperations::moveOverlapping(buffer + m_start, buffer + position, buffer + m_start + 1); + m_start = (m_start + 1) % m_buffer.capacity(); + } else { + TypeOperations::moveOverlapping(buffer + position + 1, buffer + m_end, buffer + position); + m_end = (m_end - 1 + m_buffer.capacity()) % m_buffer.capacity(); + } + checkValidity(); + } + +#ifdef NDEBUG + template<typename T, size_t inlineCapacity> inline void DequeIteratorBase<T, inlineCapacity>::checkValidity() const { } + template<typename T, size_t inlineCapacity> inline void DequeIteratorBase<T, inlineCapacity>::checkValidity(const DequeIteratorBase<T, inlineCapacity>&) const { } + template<typename T, size_t inlineCapacity> inline void DequeIteratorBase<T, inlineCapacity>::addToIteratorsList() { } + template<typename T, size_t inlineCapacity> inline void DequeIteratorBase<T, inlineCapacity>::removeFromIteratorsList() { } +#else + template<typename T, size_t inlineCapacity> + void DequeIteratorBase<T, inlineCapacity>::checkValidity() const + { + ASSERT(m_deque); + m_deque->checkIndexValidity(m_index); + } + + template<typename T, size_t inlineCapacity> + void DequeIteratorBase<T, inlineCapacity>::checkValidity(const Base& other) const + { + checkValidity(); + other.checkValidity(); + ASSERT(m_deque == other.m_deque); + } + + template<typename T, size_t inlineCapacity> + void DequeIteratorBase<T, inlineCapacity>::addToIteratorsList() + { + if (!m_deque) + m_next = 0; + else { + m_next = m_deque->m_iterators; + m_deque->m_iterators = this; + if (m_next) + m_next->m_previous = this; + } + m_previous = 0; + } + + template<typename T, size_t inlineCapacity> + void DequeIteratorBase<T, inlineCapacity>::removeFromIteratorsList() + { + if (!m_deque) { + ASSERT(!m_next); + ASSERT(!m_previous); + } else { + if (m_next) { + ASSERT(m_next->m_previous == this); + m_next->m_previous = m_previous; + } + if (m_previous) { + ASSERT(m_deque->m_iterators != this); + ASSERT(m_previous->m_next == this); + m_previous->m_next = m_next; + } else { + ASSERT(m_deque->m_iterators == this); + m_deque->m_iterators = m_next; + } + } + m_next = 0; + m_previous = 0; + } +#endif + + template<typename T, size_t inlineCapacity> + inline DequeIteratorBase<T, inlineCapacity>::DequeIteratorBase() + : m_deque(0) + { + } + + template<typename T, size_t inlineCapacity> + inline DequeIteratorBase<T, inlineCapacity>::DequeIteratorBase(const Deque<T, inlineCapacity>* deque, size_t index) + : m_deque(const_cast<Deque<T, inlineCapacity>*>(deque)) + , m_index(index) + { + addToIteratorsList(); + checkValidity(); + } + + template<typename T, size_t inlineCapacity> + inline DequeIteratorBase<T, inlineCapacity>::DequeIteratorBase(const Base& other) + : m_deque(other.m_deque) + , m_index(other.m_index) + { + addToIteratorsList(); + checkValidity(); + } + + template<typename T, size_t inlineCapacity> + inline DequeIteratorBase<T, inlineCapacity>& DequeIteratorBase<T, inlineCapacity>::operator=(const Base& other) + { + other.checkValidity(); + removeFromIteratorsList(); + + m_deque = other.m_deque; + m_index = other.m_index; + addToIteratorsList(); + checkValidity(); + return *this; + } + + template<typename T, size_t inlineCapacity> + inline DequeIteratorBase<T, inlineCapacity>::~DequeIteratorBase() + { +#ifndef NDEBUG + removeFromIteratorsList(); + m_deque = 0; +#endif + } + + template<typename T, size_t inlineCapacity> + inline bool DequeIteratorBase<T, inlineCapacity>::isEqual(const Base& other) const + { + checkValidity(other); + return m_index == other.m_index; + } + + template<typename T, size_t inlineCapacity> + inline void DequeIteratorBase<T, inlineCapacity>::increment() + { + checkValidity(); + ASSERT(m_index != m_deque->m_end); + ASSERT(m_deque->m_buffer.capacity()); + if (m_index == m_deque->m_buffer.capacity() - 1) + m_index = 0; + else + ++m_index; + checkValidity(); + } + + template<typename T, size_t inlineCapacity> + inline void DequeIteratorBase<T, inlineCapacity>::decrement() + { + checkValidity(); + ASSERT(m_index != m_deque->m_start); + ASSERT(m_deque->m_buffer.capacity()); + if (!m_index) + m_index = m_deque->m_buffer.capacity() - 1; + else + --m_index; + checkValidity(); + } + + template<typename T, size_t inlineCapacity> + inline T* DequeIteratorBase<T, inlineCapacity>::after() const + { + checkValidity(); + ASSERT(m_index != m_deque->m_end); + return &m_deque->m_buffer.buffer()[m_index]; + } + + template<typename T, size_t inlineCapacity> + inline T* DequeIteratorBase<T, inlineCapacity>::before() const + { + checkValidity(); + ASSERT(m_index != m_deque->m_start); + if (!m_index) + return &m_deque->m_buffer.buffer()[m_deque->m_buffer.capacity() - 1]; + return &m_deque->m_buffer.buffer()[m_index - 1]; + } + +} // namespace WTF + +using WTF::Deque; + +#endif // WTF_Deque_h diff --git a/Source/JavaScriptCore/wtf/DisallowCType.h b/Source/JavaScriptCore/wtf/DisallowCType.h new file mode 100644 index 000000000..436f7f214 --- /dev/null +++ b/Source/JavaScriptCore/wtf/DisallowCType.h @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2007 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of Apple Computer, Inc. ("Apple") 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 APPLE AND ITS 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 APPLE OR ITS 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. + */ + +#ifndef WTF_DisallowCType_h +#define WTF_DisallowCType_h + +// The behavior of many of the functions in the <ctype.h> header is dependent +// on the current locale. But almost all uses of these functions are for +// locale-independent, ASCII-specific purposes. In WebKit code we use our own +// ASCII-specific functions instead. This header makes sure we get a compile-time +// error if we use one of the <ctype.h> functions by accident. + +#include <ctype.h> + +#undef isalnum +#undef isalpha +#undef isascii +#undef isblank +#undef iscntrl +#undef isdigit +#undef isgraph +#undef islower +#undef isprint +#undef ispunct +#undef isspace +#undef isupper +#undef isxdigit +#undef toascii +#undef tolower +#undef toupper + +#define isalnum isalnum_WTF_Please_use_ASCIICType_instead_of_ctype_see_comment_in_ASCIICType_h +#define isalpha isalpha_WTF_Please_use_ASCIICType_instead_of_ctype_see_comment_in_ASCIICType_h +#define isascii isascii_WTF_Please_use_ASCIICType_instead_of_ctype_see_comment_in_ASCIICType_h +#define isblank isblank_WTF_Please_use_ASCIICType_instead_of_ctype_see_comment_in_ASCIICType_h +#define iscntrl iscntrl_WTF_Please_use_ASCIICType_instead_of_ctype_see_comment_in_ASCIICType_h +#define isdigit isdigit_WTF_Please_use_ASCIICType_instead_of_ctype_see_comment_in_ASCIICType_h +#define isgraph isgraph_WTF_Please_use_ASCIICType_instead_of_ctype_see_comment_in_ASCIICType_h +#define islower islower_WTF_Please_use_ASCIICType_instead_of_ctype_see_comment_in_ASCIICType_h +#define isprint isprint_WTF_Please_use_ASCIICType_instead_of_ctype_see_comment_in_ASCIICType_h +#define ispunct ispunct_WTF_Please_use_ASCIICType_instead_of_ctype_see_comment_in_ASCIICType_h +#define isspace isspace_WTF_Please_use_ASCIICType_instead_of_ctype_see_comment_in_ASCIICType_h +#define isupper isupper_WTF_Please_use_ASCIICType_instead_of_ctype_see_comment_in_ASCIICType_h +#define isxdigit isxdigit_WTF_Please_use_ASCIICType_instead_of_ctype_see_comment_in_ASCIICType_h +#define toascii toascii_WTF_Please_use_ASCIICType_instead_of_ctype_see_comment_in_ASCIICType_h +#define tolower tolower_WTF_Please_use_ASCIICType_instead_of_ctype_see_comment_in_ASCIICType_h +#define toupper toupper_WTF_Please_use_ASCIICType_instead_of_ctype_see_comment_in_ASCIICType_h + +#endif diff --git a/Source/JavaScriptCore/wtf/DoublyLinkedList.h b/Source/JavaScriptCore/wtf/DoublyLinkedList.h new file mode 100644 index 000000000..18aa00e17 --- /dev/null +++ b/Source/JavaScriptCore/wtf/DoublyLinkedList.h @@ -0,0 +1,168 @@ +/* + * Copyright (C) 2011 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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 APPLE INC. AND ITS 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 APPLE INC. OR ITS 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. + */ + +#ifndef DoublyLinkedList_h +#define DoublyLinkedList_h + +namespace WTF { + +// This class allows nodes to share code without dictating data member layout. +template<typename T> class DoublyLinkedListNode { +public: + DoublyLinkedListNode(); + + void setPrev(T*); + void setNext(T*); + + T* prev() const; + T* next() const; +}; + +template<typename T> inline DoublyLinkedListNode<T>::DoublyLinkedListNode() +{ + setPrev(0); + setNext(0); +} + +template<typename T> inline void DoublyLinkedListNode<T>::setPrev(T* prev) +{ + static_cast<T*>(this)->m_prev = prev; +} + +template<typename T> inline void DoublyLinkedListNode<T>::setNext(T* next) +{ + static_cast<T*>(this)->m_next = next; +} + +template<typename T> inline T* DoublyLinkedListNode<T>::prev() const +{ + return static_cast<const T*>(this)->m_prev; +} + +template<typename T> inline T* DoublyLinkedListNode<T>::next() const +{ + return static_cast<const T*>(this)->m_next; +} + +template<typename T> class DoublyLinkedList { +public: + DoublyLinkedList(); + + bool isEmpty() const; + size_t size() const; // This is O(n). + void clear(); + + T* head() const; + T* removeHead(); + + void append(T*); + void remove(T*); + +private: + T* m_head; + T* m_tail; +}; + +template<typename T> inline DoublyLinkedList<T>::DoublyLinkedList() + : m_head(0) + , m_tail(0) +{ +} + +template<typename T> inline bool DoublyLinkedList<T>::isEmpty() const +{ + return !m_head; +} + +template<typename T> inline size_t DoublyLinkedList<T>::size() const +{ + size_t size = 0; + for (T* node = m_head; node; node = node->next()) + ++size; + return size; +} + +template<typename T> inline void DoublyLinkedList<T>::clear() +{ + m_head = 0; + m_tail = 0; +} + +template<typename T> inline T* DoublyLinkedList<T>::head() const +{ + return m_head; +} + +template<typename T> inline void DoublyLinkedList<T>::append(T* node) +{ + if (!m_tail) { + ASSERT(!m_head); + m_head = node; + m_tail = node; + node->setPrev(0); + node->setNext(0); + return; + } + + ASSERT(m_head); + m_tail->setNext(node); + node->setPrev(m_tail); + node->setNext(0); + m_tail = node; +} + +template<typename T> inline void DoublyLinkedList<T>::remove(T* node) +{ + if (node->prev()) { + ASSERT(node != m_head); + node->prev()->setNext(node->next()); + } else { + ASSERT(node == m_head); + m_head = node->next(); + } + + if (node->next()) { + ASSERT(node != m_tail); + node->next()->setPrev(node->prev()); + } else { + ASSERT(node == m_tail); + m_tail = node->prev(); + } +} + +template<typename T> inline T* DoublyLinkedList<T>::removeHead() +{ + T* node = head(); + if (node) + remove(node); + return node; +} + +} // namespace WTF + +using WTF::DoublyLinkedListNode; +using WTF::DoublyLinkedList; + +#endif diff --git a/Source/JavaScriptCore/wtf/DynamicAnnotations.cpp b/Source/JavaScriptCore/wtf/DynamicAnnotations.cpp new file mode 100644 index 000000000..b662877a5 --- /dev/null +++ b/Source/JavaScriptCore/wtf/DynamicAnnotations.cpp @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2011 Google Inc. 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. + * * Neither the name of Google Inc. 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 "config.h" + +#include "DynamicAnnotations.h" + +#if USE(DYNAMIC_ANNOTATIONS) && !USE(DYNAMIC_ANNOTATIONS_NOIMPL) + +// Identical code folding(-Wl,--icf=all) countermeasures. +// This makes all Annotate* functions different, which prevents the linker from +// folding them. +#ifdef __COUNTER__ +#define DYNAMIC_ANNOTATIONS_IMPL \ + volatile short lineno = (__LINE__ << 8) + __COUNTER__; \ + (void)lineno; +#else +#define DYNAMIC_ANNOTATIONS_IMPL \ + volatile short lineno = (__LINE__ << 8); \ + (void)lineno; +#endif + +void WTFAnnotateBenignRaceSized(const char*, int, const volatile void*, long, const char*) +{ + DYNAMIC_ANNOTATIONS_IMPL +} + +void WTFAnnotateHappensBefore(const char*, int, const volatile void*) +{ + DYNAMIC_ANNOTATIONS_IMPL +} + +void WTFAnnotateHappensAfter(const char*, int, const volatile void*) +{ + DYNAMIC_ANNOTATIONS_IMPL +} + +#endif // USE(DYNAMIC_ANNOTATIONS) && !USE(DYNAMIC_ANNOTATIONS_NOIMPL) diff --git a/Source/JavaScriptCore/wtf/DynamicAnnotations.h b/Source/JavaScriptCore/wtf/DynamicAnnotations.h new file mode 100644 index 000000000..38acce35e --- /dev/null +++ b/Source/JavaScriptCore/wtf/DynamicAnnotations.h @@ -0,0 +1,96 @@ +/* + * Copyright (C) 2011 Google Inc. 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. + * * Neither the name of Google Inc. 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. + */ + +#ifndef WTF_DynamicAnnotations_h +#define WTF_DynamicAnnotations_h + +/* This file defines dynamic annotations for use with dynamic analysis + * tool such as ThreadSanitizer, Valgrind, etc. + * + * Dynamic annotation is a source code annotation that affects + * the generated code (that is, the annotation is not a comment). + * Each such annotation is attached to a particular + * instruction and/or to a particular object (address) in the program. + * + * By using dynamic annotations a developer can give more details to the dynamic + * analysis tool to improve its precision. + * + * In C/C++ program the annotations are represented as C macros. + * With the default build flags, these macros are empty, hence don't affect + * performance of a compiled binary. + * If dynamic annotations are enabled, they just call no-op functions. + * The dynamic analysis tools can intercept these functions and replace them + * with their own implementations. + * + * See http://code.google.com/p/data-race-test/wiki/DynamicAnnotations for more information. + */ + +#if USE(DYNAMIC_ANNOTATIONS) +/* Tell data race detector that we're not interested in reports on the given address range. */ +#define WTF_ANNOTATE_BENIGN_RACE_SIZED(address, size, description) WTFAnnotateBenignRaceSized(__FILE__, __LINE__, address, size, description) +#define WTF_ANNOTATE_BENIGN_RACE(pointer, description) WTFAnnotateBenignRaceSized(__FILE__, __LINE__, pointer, sizeof(*(pointer)), description) + +/* Annotations for user-defined synchronization mechanisms. + * These annotations can be used to define happens-before arcs in user-defined + * synchronization mechanisms: the race detector will infer an arc from + * the former to the latter when they share the same argument pointer. + * + * The most common case requiring annotations is atomic reference counting: + * bool deref() { + * ANNOTATE_HAPPENS_BEFORE(&m_refCount); + * if (!atomicDecrement(&m_refCount)) { + * // m_refCount is now 0 + * ANNOTATE_HAPPENS_AFTER(&m_refCount); + * // "return true; happens-after each atomicDecrement of m_refCount" + * return true; + * } + * return false; + * } + */ +#define WTF_ANNOTATE_HAPPENS_BEFORE(address) WTFAnnotateHappensBefore(__FILE__, __LINE__, address) +#define WTF_ANNOTATE_HAPPENS_AFTER(address) WTFAnnotateHappensAfter(__FILE__, __LINE__, address) + +#ifdef __cplusplus +extern "C" { +#endif +/* Don't use these directly, use the above macros instead. */ +void WTFAnnotateBenignRaceSized(const char* file, int line, const volatile void* memory, long size, const char* description); +void WTFAnnotateHappensBefore(const char* file, int line, const volatile void* address); +void WTFAnnotateHappensAfter(const char* file, int line, const volatile void* address); +#ifdef __cplusplus +} // extern "C" +#endif + +#else // USE(DYNAMIC_ANNOTATIONS) +/* These macros are empty when dynamic annotations are not enabled so you can + * use them without affecting the performance of release binaries. */ +#define WTF_ANNOTATE_BENIGN_RACE_SIZED(address, size, description) +#define WTF_ANNOTATE_BENIGN_RACE(pointer, description) +#define WTF_ANNOTATE_HAPPENS_BEFORE(address) +#define WTF_ANNOTATE_HAPPENS_AFTER(address) +#endif // USE(DYNAMIC_ANNOTATIONS) + +#endif // WTF_DynamicAnnotations_h diff --git a/Source/JavaScriptCore/wtf/Encoder.h b/Source/JavaScriptCore/wtf/Encoder.h new file mode 100644 index 000000000..109b0db8d --- /dev/null +++ b/Source/JavaScriptCore/wtf/Encoder.h @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2011 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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 APPLE INC. ``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 APPLE INC. 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. + */ + +#ifndef Encoder_h +#define Encoder_h + +#include <stdint.h> + +namespace WTF { + +class String; + +class Encoder { +protected: + Encoder() { } + virtual ~Encoder() { } + +public: + virtual void encodeBytes(const uint8_t*, size_t) = 0; + + virtual void encodeBool(bool) = 0; + virtual void encodeUInt32(uint32_t) = 0; + virtual void encodeUInt64(uint64_t) = 0; + virtual void encodeInt32(int32_t) = 0; + virtual void encodeInt64(int64_t) = 0; + virtual void encodeFloat(float) = 0; + virtual void encodeDouble(double) = 0; + virtual void encodeString(const String&) = 0; +}; + +} // namespace WTF + +using WTF::Encoder; + +#endif // Encoder_h diff --git a/Source/JavaScriptCore/wtf/ExportMacros.h b/Source/JavaScriptCore/wtf/ExportMacros.h new file mode 100644 index 000000000..a6b3bce5a --- /dev/null +++ b/Source/JavaScriptCore/wtf/ExportMacros.h @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2011 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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 APPLE COMPUTER, INC. ``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 APPLE COMPUTER, INC. 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. + * + * This file handles shared library symbol export decorations. It is recommended + * that all WebKit projects use these definitions so that symbol exports work + * properly on all platforms and compilers that WebKit builds under. + */ + +#ifndef ExportMacros_h +#define ExportMacros_h + +#include "Platform.h" + +// See note in wtf/Platform.h for more info on EXPORT_MACROS. +#if USE(EXPORT_MACROS) + +#if !PLATFORM(CHROMIUM) && OS(WINDOWS) && !COMPILER(GCC) +#define WTF_EXPORT __declspec(dllexport) +#define WTF_IMPORT __declspec(dllimport) +#define WTF_HIDDEN +#elif defined(__GNUC__) && !defined(__CC_ARM) && !defined(__ARMCC__) +#define WTF_EXPORT __attribute__((visibility("default"))) +#define WTF_IMPORT WTF_EXPORT +#define WTF_HIDDEN __attribute__((visibility("hidden"))) +#else +#define WTF_EXPORT +#define WTF_IMPORT +#define WTF_HIDDEN +#endif + +// FIXME: When all ports are using the export macros, we should replace +// WTF_EXPORTDATA with WTF_EXPORT_PRIVATE macros. +#if defined(BUILDING_WTF) +#define WTF_EXPORTDATA WTF_EXPORT +#else +#define WTF_EXPORTDATA WTF_IMPORT +#endif + +#else // !USE(EXPORT_MACROS) + +#if !PLATFORM(CHROMIUM) && OS(WINDOWS) && !COMPILER(GCC) +#if defined(BUILDING_WTF) +#define WTF_EXPORTDATA __declspec(dllexport) +#else +#define WTF_EXPORTDATA __declspec(dllimport) +#endif +#else // PLATFORM(CHROMIUM) || !OS(WINDOWS) || COMPILER(GCC) +#define WTF_EXPORTDATA +#endif // !PLATFORM(CHROMIUM)... + +#define WTF_EXPORTCLASS WTF_EXPORTDATA + +#define WTF_EXPORT +#define WTF_IMPORT +#define WTF_HIDDEN + +#endif // USE(EXPORT_MACROS) + +#if defined(BUILDING_WTF) +#define WTF_EXPORT_PRIVATE WTF_EXPORT +#else +#define WTF_EXPORT_PRIVATE WTF_IMPORT +#endif + +#define WTF_EXPORT_HIDDEN WTF_HIDDEN + +#define HIDDEN_INLINE WTF_EXPORT_HIDDEN inline + +#endif // ExportMacros_h diff --git a/Source/JavaScriptCore/wtf/FastAllocBase.h b/Source/JavaScriptCore/wtf/FastAllocBase.h new file mode 100644 index 000000000..1edbed94d --- /dev/null +++ b/Source/JavaScriptCore/wtf/FastAllocBase.h @@ -0,0 +1,427 @@ +/* + * Copyright (C) 2008, 2009 Paul Pedriana <ppedriana@ea.com>. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of Apple Computer, Inc. ("Apple") 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 APPLE AND ITS 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 APPLE OR ITS 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. + */ + +#ifndef FastAllocBase_h +#define FastAllocBase_h + +// Provides customizable overrides of fastMalloc/fastFree and operator new/delete +// +// Provided functionality: +// Macro: WTF_MAKE_FAST_ALLOCATED +// namespace WTF { +// +// T* fastNew<T>(); +// T* fastNew<T>(arg); +// T* fastNew<T>(arg, arg); +// T* fastNewArray<T>(count); +// void fastDelete(T* p); +// void fastDeleteArray(T* p); +// void fastNonNullDelete(T* p); +// void fastNonNullDeleteArray(T* p); +// } +// +// FastDelete assumes that the underlying +// +// Example usage: +// class Widget { +// WTF_MAKE_FAST_ALLOCATED +// ... +// }; +// +// struct Data { +// WTF_MAKE_FAST_ALLOCATED +// public: +// ... +// }; +// +// char* charPtr = fastNew<char>(); +// fastDelete(charPtr); +// +// char* charArrayPtr = fastNewArray<char>(37); +// fastDeleteArray(charArrayPtr); +// +// void** voidPtrPtr = fastNew<void*>(); +// fastDelete(voidPtrPtr); +// +// void** voidPtrArrayPtr = fastNewArray<void*>(37); +// fastDeleteArray(voidPtrArrayPtr); +// +// POD* podPtr = fastNew<POD>(); +// fastDelete(podPtr); +// +// POD* podArrayPtr = fastNewArray<POD>(37); +// fastDeleteArray(podArrayPtr); +// +// Object* objectPtr = fastNew<Object>(); +// fastDelete(objectPtr); +// +// Object* objectArrayPtr = fastNewArray<Object>(37); +// fastDeleteArray(objectArrayPtr); +// + +#include <new> +#include <stdint.h> +#include <stdlib.h> +#include <string.h> +#include "Assertions.h" +#include "FastMalloc.h" +#include "StdLibExtras.h" +#include "TypeTraits.h" + +#define WTF_MAKE_FAST_ALLOCATED \ +public: \ + void* operator new(size_t, void* p) { return p; } \ + void* operator new[](size_t, void* p) { return p; } \ + \ + void* operator new(size_t size) \ + { \ + void* p = ::WTF::fastMalloc(size); \ + ::WTF::fastMallocMatchValidateMalloc(p, ::WTF::Internal::AllocTypeClassNew); \ + return p; \ + } \ + \ + void operator delete(void* p) \ + { \ + ::WTF::fastMallocMatchValidateFree(p, ::WTF::Internal::AllocTypeClassNew); \ + ::WTF::fastFree(p); \ + } \ + \ + void* operator new[](size_t size) \ + { \ + void* p = ::WTF::fastMalloc(size); \ + ::WTF::fastMallocMatchValidateMalloc(p, ::WTF::Internal::AllocTypeClassNewArray); \ + return p; \ + } \ + \ + void operator delete[](void* p) \ + { \ + ::WTF::fastMallocMatchValidateFree(p, ::WTF::Internal::AllocTypeClassNewArray); \ + ::WTF::fastFree(p); \ + } \ + void* operator new(size_t, NotNullTag, void* location) \ + { \ + ASSERT(location); \ + return location; \ + } \ +private: \ +typedef int ThisIsHereToForceASemicolonAfterThisMacro + +namespace WTF { + + // fastNew / fastDelete + + template <typename T> + inline T* fastNew() + { + void* p = fastMalloc(sizeof(T)); + + if (!p) + return 0; + + fastMallocMatchValidateMalloc(p, Internal::AllocTypeFastNew); + return ::new (p) T; + } + + template <typename T, typename Arg1> + inline T* fastNew(Arg1 arg1) + { + void* p = fastMalloc(sizeof(T)); + + if (!p) + return 0; + + fastMallocMatchValidateMalloc(p, Internal::AllocTypeFastNew); + return ::new (p) T(arg1); + } + + template <typename T, typename Arg1, typename Arg2> + inline T* fastNew(Arg1 arg1, Arg2 arg2) + { + void* p = fastMalloc(sizeof(T)); + + if (!p) + return 0; + + fastMallocMatchValidateMalloc(p, Internal::AllocTypeFastNew); + return ::new (p) T(arg1, arg2); + } + + template <typename T, typename Arg1, typename Arg2, typename Arg3> + inline T* fastNew(Arg1 arg1, Arg2 arg2, Arg3 arg3) + { + void* p = fastMalloc(sizeof(T)); + + if (!p) + return 0; + + fastMallocMatchValidateMalloc(p, Internal::AllocTypeFastNew); + return ::new (p) T(arg1, arg2, arg3); + } + + template <typename T, typename Arg1, typename Arg2, typename Arg3, typename Arg4> + inline T* fastNew(Arg1 arg1, Arg2 arg2, Arg3 arg3, Arg4 arg4) + { + void* p = fastMalloc(sizeof(T)); + + if (!p) + return 0; + + fastMallocMatchValidateMalloc(p, Internal::AllocTypeFastNew); + return ::new (p) T(arg1, arg2, arg3, arg4); + } + + template <typename T, typename Arg1, typename Arg2, typename Arg3, typename Arg4, typename Arg5> + inline T* fastNew(Arg1 arg1, Arg2 arg2, Arg3 arg3, Arg4 arg4, Arg5 arg5) + { + void* p = fastMalloc(sizeof(T)); + + if (!p) + return 0; + + fastMallocMatchValidateMalloc(p, Internal::AllocTypeFastNew); + return ::new (p) T(arg1, arg2, arg3, arg4, arg5); + } + + namespace Internal { + + // We define a union of pointer to an integer and pointer to T. + // When non-POD arrays are allocated we add a few leading bytes to tell what + // the size of the array is. We return to the user the pointer to T. + // The way to think of it is as if we allocate a struct like so: + // struct Array { + // AllocAlignmentInteger m_size; + // T m_T[array count]; + // }; + + template <typename T> + union ArraySize { + AllocAlignmentInteger* size; + T* t; + }; + + // This is a support template for fastNewArray. + // This handles the case wherein T has a trivial ctor and a trivial dtor. + template <typename T, bool trivialCtor, bool trivialDtor> + struct NewArrayImpl { + static T* fastNewArray(size_t count) + { + T* p = static_cast<T*>(fastMalloc(sizeof(T) * count)); + fastMallocMatchValidateMalloc(p, Internal::AllocTypeFastNewArray); + return p; + } + }; + + // This is a support template for fastNewArray. + // This handles the case wherein T has a non-trivial ctor and a trivial dtor. + template <typename T> + struct NewArrayImpl<T, false, true> { + static T* fastNewArray(size_t count) + { + T* p = static_cast<T*>(fastMalloc(sizeof(T) * count)); + + if (!p) + return 0; + + fastMallocMatchValidateMalloc(p, Internal::AllocTypeFastNewArray); + + for (T* pObject = p, *pObjectEnd = pObject + count; pObject != pObjectEnd; ++pObject) + ::new (pObject) T; + + return p; + } + }; + + // This is a support template for fastNewArray. + // This handles the case wherein T has a trivial ctor and a non-trivial dtor. + template <typename T> + struct NewArrayImpl<T, true, false> { + static T* fastNewArray(size_t count) + { + void* p = fastMalloc(sizeof(AllocAlignmentInteger) + (sizeof(T) * count)); + ArraySize<T> a = { static_cast<AllocAlignmentInteger*>(p) }; + + if (!p) + return 0; + + fastMallocMatchValidateMalloc(p, Internal::AllocTypeFastNewArray); + *a.size++ = count; + // No need to construct the objects in this case. + + return a.t; + } + }; + + // This is a support template for fastNewArray. + // This handles the case wherein T has a non-trivial ctor and a non-trivial dtor. + template <typename T> + struct NewArrayImpl<T, false, false> { + static T* fastNewArray(size_t count) + { + void* p = fastMalloc(sizeof(AllocAlignmentInteger) + (sizeof(T) * count)); + ArraySize<T> a = { static_cast<AllocAlignmentInteger*>(p) }; + + if (!p) + return 0; + + fastMallocMatchValidateMalloc(p, Internal::AllocTypeFastNewArray); + *a.size++ = count; + + for (T* pT = a.t, *pTEnd = pT + count; pT != pTEnd; ++pT) + ::new (pT) T; + + return a.t; + } + }; + } // namespace Internal + + template <typename T> + inline T* fastNewArray(size_t count) + { + return Internal::NewArrayImpl<T, WTF::HasTrivialConstructor<T>::value, WTF::HasTrivialDestructor<T>::value>::fastNewArray(count); + } + + template <typename T> + inline void fastDelete(T* p) + { + if (!p) + return; + + fastMallocMatchValidateFree(p, Internal::AllocTypeFastNew); + p->~T(); + fastFree(p); + } + + template <typename T> + inline void fastDeleteSkippingDestructor(T* p) + { + if (!p) + return; + + fastMallocMatchValidateFree(p, Internal::AllocTypeFastNew); + fastFree(p); + } + + namespace Internal { + // This is a support template for fastDeleteArray. + // This handles the case wherein T has a trivial dtor. + template <typename T, bool trivialDtor> + struct DeleteArrayImpl { + static void fastDeleteArray(void* p) + { + // No need to destruct the objects in this case. + // We expect that fastFree checks for null. + fastMallocMatchValidateFree(p, Internal::AllocTypeFastNewArray); + fastFree(p); + } + }; + + // This is a support template for fastDeleteArray. + // This handles the case wherein T has a non-trivial dtor. + template <typename T> + struct DeleteArrayImpl<T, false> { + static void fastDeleteArray(T* p) + { + if (!p) + return; + + ArraySize<T> a; + a.t = p; + a.size--; // Decrement size pointer + + T* pEnd = p + *a.size; + while (pEnd-- != p) + pEnd->~T(); + + fastMallocMatchValidateFree(a.size, Internal::AllocTypeFastNewArray); + fastFree(a.size); + } + }; + + } // namespace Internal + + template <typename T> + void fastDeleteArray(T* p) + { + Internal::DeleteArrayImpl<T, WTF::HasTrivialDestructor<T>::value>::fastDeleteArray(p); + } + + + template <typename T> + inline void fastNonNullDelete(T* p) + { + fastMallocMatchValidateFree(p, Internal::AllocTypeFastNew); + p->~T(); + fastFree(p); + } + + namespace Internal { + // This is a support template for fastDeleteArray. + // This handles the case wherein T has a trivial dtor. + template <typename T, bool trivialDtor> + struct NonNullDeleteArrayImpl { + static void fastNonNullDeleteArray(void* p) + { + fastMallocMatchValidateFree(p, Internal::AllocTypeFastNewArray); + // No need to destruct the objects in this case. + fastFree(p); + } + }; + + // This is a support template for fastDeleteArray. + // This handles the case wherein T has a non-trivial dtor. + template <typename T> + struct NonNullDeleteArrayImpl<T, false> { + static void fastNonNullDeleteArray(T* p) + { + ArraySize<T> a; + a.t = p; + a.size--; + + T* pEnd = p + *a.size; + while (pEnd-- != p) + pEnd->~T(); + + fastMallocMatchValidateFree(a.size, Internal::AllocTypeFastNewArray); + fastFree(a.size); + } + }; + + } // namespace Internal + + template <typename T> + void fastNonNullDeleteArray(T* p) + { + Internal::NonNullDeleteArrayImpl<T, WTF::HasTrivialDestructor<T>::value>::fastNonNullDeleteArray(p); + } + + +} // namespace WTF + +using WTF::fastDeleteSkippingDestructor; + +#endif // FastAllocBase_h diff --git a/Source/JavaScriptCore/wtf/FastMalloc.cpp b/Source/JavaScriptCore/wtf/FastMalloc.cpp new file mode 100644 index 000000000..5c684500f --- /dev/null +++ b/Source/JavaScriptCore/wtf/FastMalloc.cpp @@ -0,0 +1,4708 @@ +// Copyright (c) 2005, 2007, Google Inc. +// All rights reserved. +// Copyright (C) 2005, 2006, 2007, 2008, 2009, 2011 Apple Inc. 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 Google Inc. 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. + +// --- +// Author: Sanjay Ghemawat <opensource@google.com> +// +// A malloc that uses a per-thread cache to satisfy small malloc requests. +// (The time for malloc/free of a small object drops from 300 ns to 50 ns.) +// +// See doc/tcmalloc.html for a high-level +// description of how this malloc works. +// +// SYNCHRONIZATION +// 1. The thread-specific lists are accessed without acquiring any locks. +// This is safe because each such list is only accessed by one thread. +// 2. We have a lock per central free-list, and hold it while manipulating +// the central free list for a particular size. +// 3. The central page allocator is protected by "pageheap_lock". +// 4. The pagemap (which maps from page-number to descriptor), +// can be read without holding any locks, and written while holding +// the "pageheap_lock". +// 5. To improve performance, a subset of the information one can get +// from the pagemap is cached in a data structure, pagemap_cache_, +// that atomically reads and writes its entries. This cache can be +// read and written without locking. +// +// This multi-threaded access to the pagemap is safe for fairly +// subtle reasons. We basically assume that when an object X is +// allocated by thread A and deallocated by thread B, there must +// have been appropriate synchronization in the handoff of object +// X from thread A to thread B. The same logic applies to pagemap_cache_. +// +// THE PAGEID-TO-SIZECLASS CACHE +// Hot PageID-to-sizeclass mappings are held by pagemap_cache_. If this cache +// returns 0 for a particular PageID then that means "no information," not that +// the sizeclass is 0. The cache may have stale information for pages that do +// not hold the beginning of any free()'able object. Staleness is eliminated +// in Populate() for pages with sizeclass > 0 objects, and in do_malloc() and +// do_memalign() for all other relevant pages. +// +// TODO: Bias reclamation to larger addresses +// TODO: implement mallinfo/mallopt +// TODO: Better testing +// +// 9/28/2003 (new page-level allocator replaces ptmalloc2): +// * malloc/free of small objects goes from ~300 ns to ~50 ns. +// * allocation of a reasonably complicated struct +// goes from about 1100 ns to about 300 ns. + +#include "config.h" +#include "FastMalloc.h" + +#include "Assertions.h" +#include <limits> +#if OS(WINDOWS) +#include <windows.h> +#else +#include <pthread.h> +#endif +#include <wtf/StdLibExtras.h> +#include <string.h> + +#ifndef NO_TCMALLOC_SAMPLES +#ifdef WTF_CHANGES +#define NO_TCMALLOC_SAMPLES +#endif +#endif + +#if !(defined(USE_SYSTEM_MALLOC) && USE_SYSTEM_MALLOC) && defined(NDEBUG) +#define FORCE_SYSTEM_MALLOC 0 +#else +#define FORCE_SYSTEM_MALLOC 1 +#endif + +// Use a background thread to periodically scavenge memory to release back to the system +#if PLATFORM(IOS) +#define USE_BACKGROUND_THREAD_TO_SCAVENGE_MEMORY 0 +#else +#define USE_BACKGROUND_THREAD_TO_SCAVENGE_MEMORY 1 +#endif + +#ifndef NDEBUG +namespace WTF { + +#if OS(WINDOWS) + +// TLS_OUT_OF_INDEXES is not defined on WinCE. +#ifndef TLS_OUT_OF_INDEXES +#define TLS_OUT_OF_INDEXES 0xffffffff +#endif + +static DWORD isForibiddenTlsIndex = TLS_OUT_OF_INDEXES; +static const LPVOID kTlsAllowValue = reinterpret_cast<LPVOID>(0); // Must be zero. +static const LPVOID kTlsForbiddenValue = reinterpret_cast<LPVOID>(1); + +#if !ASSERT_DISABLED +static bool isForbidden() +{ + // By default, fastMalloc is allowed so we don't allocate the + // tls index unless we're asked to make it forbidden. If TlsSetValue + // has not been called on a thread, the value returned by TlsGetValue is 0. + return (isForibiddenTlsIndex != TLS_OUT_OF_INDEXES) && (TlsGetValue(isForibiddenTlsIndex) == kTlsForbiddenValue); +} +#endif + +void fastMallocForbid() +{ + if (isForibiddenTlsIndex == TLS_OUT_OF_INDEXES) + isForibiddenTlsIndex = TlsAlloc(); // a little racey, but close enough for debug only + TlsSetValue(isForibiddenTlsIndex, kTlsForbiddenValue); +} + +void fastMallocAllow() +{ + if (isForibiddenTlsIndex == TLS_OUT_OF_INDEXES) + return; + TlsSetValue(isForibiddenTlsIndex, kTlsAllowValue); +} + +#else // !OS(WINDOWS) + +static pthread_key_t isForbiddenKey; +static pthread_once_t isForbiddenKeyOnce = PTHREAD_ONCE_INIT; +static void initializeIsForbiddenKey() +{ + pthread_key_create(&isForbiddenKey, 0); +} + +#if !ASSERT_DISABLED +static bool isForbidden() +{ + pthread_once(&isForbiddenKeyOnce, initializeIsForbiddenKey); + return !!pthread_getspecific(isForbiddenKey); +} +#endif + +void fastMallocForbid() +{ + pthread_once(&isForbiddenKeyOnce, initializeIsForbiddenKey); + pthread_setspecific(isForbiddenKey, &isForbiddenKey); +} + +void fastMallocAllow() +{ + pthread_once(&isForbiddenKeyOnce, initializeIsForbiddenKey); + pthread_setspecific(isForbiddenKey, 0); +} +#endif // OS(WINDOWS) + +} // namespace WTF +#endif // NDEBUG + +namespace WTF { + + +namespace Internal { +#if !ENABLE(WTF_MALLOC_VALIDATION) +void fastMallocMatchFailed(void*); +#else +COMPILE_ASSERT(((sizeof(ValidationHeader) % sizeof(AllocAlignmentInteger)) == 0), ValidationHeader_must_produce_correct_alignment); +#endif + +NO_RETURN_DUE_TO_CRASH void fastMallocMatchFailed(void*) +{ + CRASH(); +} + +} // namespace Internal + + +void* fastZeroedMalloc(size_t n) +{ + void* result = fastMalloc(n); + memset(result, 0, n); + return result; +} + +char* fastStrDup(const char* src) +{ + size_t len = strlen(src) + 1; + char* dup = static_cast<char*>(fastMalloc(len)); + memcpy(dup, src, len); + return dup; +} + +TryMallocReturnValue tryFastZeroedMalloc(size_t n) +{ + void* result; + if (!tryFastMalloc(n).getValue(result)) + return 0; + memset(result, 0, n); + return result; +} + +} // namespace WTF + +#if FORCE_SYSTEM_MALLOC + +#if OS(DARWIN) +#include <malloc/malloc.h> +#elif OS(WINDOWS) +#include <malloc.h> +#endif + +namespace WTF { + +TryMallocReturnValue tryFastMalloc(size_t n) +{ + ASSERT(!isForbidden()); + +#if ENABLE(WTF_MALLOC_VALIDATION) + if (std::numeric_limits<size_t>::max() - Internal::ValidationBufferSize <= n) // If overflow would occur... + return 0; + + void* result = malloc(n + Internal::ValidationBufferSize); + if (!result) + return 0; + Internal::ValidationHeader* header = static_cast<Internal::ValidationHeader*>(result); + header->m_size = n; + header->m_type = Internal::AllocTypeMalloc; + header->m_prefix = static_cast<unsigned>(Internal::ValidationPrefix); + result = header + 1; + *Internal::fastMallocValidationSuffix(result) = Internal::ValidationSuffix; + fastMallocValidate(result); + return result; +#else + return malloc(n); +#endif +} + +void* fastMalloc(size_t n) +{ + ASSERT(!isForbidden()); + +#if ENABLE(WTF_MALLOC_VALIDATION) + TryMallocReturnValue returnValue = tryFastMalloc(n); + void* result; + if (!returnValue.getValue(result)) + CRASH(); +#else + void* result = malloc(n); +#endif + + if (!result) + CRASH(); + + return result; +} + +TryMallocReturnValue tryFastCalloc(size_t n_elements, size_t element_size) +{ + ASSERT(!isForbidden()); + +#if ENABLE(WTF_MALLOC_VALIDATION) + size_t totalBytes = n_elements * element_size; + if (n_elements > 1 && element_size && (totalBytes / element_size) != n_elements) + return 0; + + TryMallocReturnValue returnValue = tryFastMalloc(totalBytes); + void* result; + if (!returnValue.getValue(result)) + return 0; + memset(result, 0, totalBytes); + fastMallocValidate(result); + return result; +#else + return calloc(n_elements, element_size); +#endif +} + +void* fastCalloc(size_t n_elements, size_t element_size) +{ + ASSERT(!isForbidden()); + +#if ENABLE(WTF_MALLOC_VALIDATION) + TryMallocReturnValue returnValue = tryFastCalloc(n_elements, element_size); + void* result; + if (!returnValue.getValue(result)) + CRASH(); +#else + void* result = calloc(n_elements, element_size); +#endif + + if (!result) + CRASH(); + + return result; +} + +void fastFree(void* p) +{ + ASSERT(!isForbidden()); + +#if ENABLE(WTF_MALLOC_VALIDATION) + if (!p) + return; + + fastMallocMatchValidateFree(p, Internal::AllocTypeMalloc); + Internal::ValidationHeader* header = Internal::fastMallocValidationHeader(p); + memset(p, 0xCC, header->m_size); + free(header); +#else + free(p); +#endif +} + +TryMallocReturnValue tryFastRealloc(void* p, size_t n) +{ + ASSERT(!isForbidden()); + +#if ENABLE(WTF_MALLOC_VALIDATION) + if (p) { + if (std::numeric_limits<size_t>::max() - Internal::ValidationBufferSize <= n) // If overflow would occur... + return 0; + fastMallocValidate(p); + Internal::ValidationHeader* result = static_cast<Internal::ValidationHeader*>(realloc(Internal::fastMallocValidationHeader(p), n + Internal::ValidationBufferSize)); + if (!result) + return 0; + result->m_size = n; + result = result + 1; + *fastMallocValidationSuffix(result) = Internal::ValidationSuffix; + fastMallocValidate(result); + return result; + } else { + return fastMalloc(n); + } +#else + return realloc(p, n); +#endif +} + +void* fastRealloc(void* p, size_t n) +{ + ASSERT(!isForbidden()); + +#if ENABLE(WTF_MALLOC_VALIDATION) + TryMallocReturnValue returnValue = tryFastRealloc(p, n); + void* result; + if (!returnValue.getValue(result)) + CRASH(); +#else + void* result = realloc(p, n); +#endif + + if (!result) + CRASH(); + return result; +} + +void releaseFastMallocFreeMemory() { } + +FastMallocStatistics fastMallocStatistics() +{ + FastMallocStatistics statistics = { 0, 0, 0 }; + return statistics; +} + +size_t fastMallocSize(const void* p) +{ +#if ENABLE(WTF_MALLOC_VALIDATION) + return Internal::fastMallocValidationHeader(const_cast<void*>(p))->m_size; +#elif OS(DARWIN) + return malloc_size(p); +#elif OS(WINDOWS) + return _msize(const_cast<void*>(p)); +#else + return 1; +#endif +} + +} // namespace WTF + +#if OS(DARWIN) +// This symbol is present in the JavaScriptCore exports file even when FastMalloc is disabled. +// It will never be used in this case, so it's type and value are less interesting than its presence. +extern "C" const int jscore_fastmalloc_introspection = 0; +#endif + +#else // FORCE_SYSTEM_MALLOC + +#if HAVE(STDINT_H) +#include <stdint.h> +#elif HAVE(INTTYPES_H) +#include <inttypes.h> +#else +#include <sys/types.h> +#endif + +#include "AlwaysInline.h" +#include "Assertions.h" +#include "TCPackedCache.h" +#include "TCPageMap.h" +#include "TCSpinLock.h" +#include "TCSystemAlloc.h" +#include <algorithm> +#include <limits> +#include <pthread.h> +#include <stdarg.h> +#include <stddef.h> +#include <stdio.h> +#if HAVE(ERRNO_H) +#include <errno.h> +#endif +#if OS(UNIX) +#include <unistd.h> +#endif +#if OS(WINDOWS) +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN +#endif +#include <windows.h> +#endif + +#ifdef WTF_CHANGES + +#if OS(DARWIN) +#include "MallocZoneSupport.h" +#include <wtf/HashSet.h> +#include <wtf/Vector.h> +#endif + +#if HAVE(HEADER_DETECTION_H) +#include "HeaderDetection.h" +#endif + +#if HAVE(DISPATCH_H) +#include <dispatch/dispatch.h> +#endif + +#if HAVE(PTHREAD_MACHDEP_H) +#include <System/pthread_machdep.h> + +#if defined(__PTK_FRAMEWORK_JAVASCRIPTCORE_KEY0) +#define WTF_USE_PTHREAD_GETSPECIFIC_DIRECT 1 +#endif +#endif + +#ifndef PRIuS +#define PRIuS "zu" +#endif + +// Calling pthread_getspecific through a global function pointer is faster than a normal +// call to the function on Mac OS X, and it's used in performance-critical code. So we +// use a function pointer. But that's not necessarily faster on other platforms, and we had +// problems with this technique on Windows, so we'll do this only on Mac OS X. +#if OS(DARWIN) +#if !USE(PTHREAD_GETSPECIFIC_DIRECT) +static void* (*pthread_getspecific_function_pointer)(pthread_key_t) = pthread_getspecific; +#define pthread_getspecific(key) pthread_getspecific_function_pointer(key) +#else +#define pthread_getspecific(key) _pthread_getspecific_direct(key) +#define pthread_setspecific(key, val) _pthread_setspecific_direct(key, (val)) +#endif +#endif + +#define DEFINE_VARIABLE(type, name, value, meaning) \ + namespace FLAG__namespace_do_not_use_directly_use_DECLARE_##type##_instead { \ + type FLAGS_##name(value); \ + char FLAGS_no##name; \ + } \ + using FLAG__namespace_do_not_use_directly_use_DECLARE_##type##_instead::FLAGS_##name + +#define DEFINE_int64(name, value, meaning) \ + DEFINE_VARIABLE(int64_t, name, value, meaning) + +#define DEFINE_double(name, value, meaning) \ + DEFINE_VARIABLE(double, name, value, meaning) + +namespace WTF { + +#define malloc fastMalloc +#define calloc fastCalloc +#define free fastFree +#define realloc fastRealloc + +#define MESSAGE LOG_ERROR +#define CHECK_CONDITION ASSERT + +#if OS(DARWIN) +struct Span; +class TCMalloc_Central_FreeListPadded; +class TCMalloc_PageHeap; +class TCMalloc_ThreadCache; +template <typename T> class PageHeapAllocator; + +class FastMallocZone { +public: + static void init(); + + static kern_return_t enumerate(task_t, void*, unsigned typeMmask, vm_address_t zoneAddress, memory_reader_t, vm_range_recorder_t); + static size_t goodSize(malloc_zone_t*, size_t size) { return size; } + static boolean_t check(malloc_zone_t*) { return true; } + static void print(malloc_zone_t*, boolean_t) { } + static void log(malloc_zone_t*, void*) { } + static void forceLock(malloc_zone_t*) { } + static void forceUnlock(malloc_zone_t*) { } + static void statistics(malloc_zone_t*, malloc_statistics_t* stats) { memset(stats, 0, sizeof(malloc_statistics_t)); } + +private: + FastMallocZone(TCMalloc_PageHeap*, TCMalloc_ThreadCache**, TCMalloc_Central_FreeListPadded*, PageHeapAllocator<Span>*, PageHeapAllocator<TCMalloc_ThreadCache>*); + static size_t size(malloc_zone_t*, const void*); + static void* zoneMalloc(malloc_zone_t*, size_t); + static void* zoneCalloc(malloc_zone_t*, size_t numItems, size_t size); + static void zoneFree(malloc_zone_t*, void*); + static void* zoneRealloc(malloc_zone_t*, void*, size_t); + static void* zoneValloc(malloc_zone_t*, size_t) { LOG_ERROR("valloc is not supported"); return 0; } + static void zoneDestroy(malloc_zone_t*) { } + + malloc_zone_t m_zone; + TCMalloc_PageHeap* m_pageHeap; + TCMalloc_ThreadCache** m_threadHeaps; + TCMalloc_Central_FreeListPadded* m_centralCaches; + PageHeapAllocator<Span>* m_spanAllocator; + PageHeapAllocator<TCMalloc_ThreadCache>* m_pageHeapAllocator; +}; + +#endif + +#endif + +#ifndef WTF_CHANGES +// This #ifdef should almost never be set. Set NO_TCMALLOC_SAMPLES if +// you're porting to a system where you really can't get a stacktrace. +#ifdef NO_TCMALLOC_SAMPLES +// We use #define so code compiles even if you #include stacktrace.h somehow. +# define GetStackTrace(stack, depth, skip) (0) +#else +# include <google/stacktrace.h> +#endif +#endif + +// Even if we have support for thread-local storage in the compiler +// and linker, the OS may not support it. We need to check that at +// runtime. Right now, we have to keep a manual set of "bad" OSes. +#if defined(HAVE_TLS) + static bool kernel_supports_tls = false; // be conservative + static inline bool KernelSupportsTLS() { + return kernel_supports_tls; + } +# if !HAVE_DECL_UNAME // if too old for uname, probably too old for TLS + static void CheckIfKernelSupportsTLS() { + kernel_supports_tls = false; + } +# else +# include <sys/utsname.h> // DECL_UNAME checked for <sys/utsname.h> too + static void CheckIfKernelSupportsTLS() { + struct utsname buf; + if (uname(&buf) != 0) { // should be impossible + MESSAGE("uname failed assuming no TLS support (errno=%d)\n", errno); + kernel_supports_tls = false; + } else if (strcasecmp(buf.sysname, "linux") == 0) { + // The linux case: the first kernel to support TLS was 2.6.0 + if (buf.release[0] < '2' && buf.release[1] == '.') // 0.x or 1.x + kernel_supports_tls = false; + else if (buf.release[0] == '2' && buf.release[1] == '.' && + buf.release[2] >= '0' && buf.release[2] < '6' && + buf.release[3] == '.') // 2.0 - 2.5 + kernel_supports_tls = false; + else + kernel_supports_tls = true; + } else { // some other kernel, we'll be optimisitic + kernel_supports_tls = true; + } + // TODO(csilvers): VLOG(1) the tls status once we support RAW_VLOG + } +# endif // HAVE_DECL_UNAME +#endif // HAVE_TLS + +// __THROW is defined in glibc systems. It means, counter-intuitively, +// "This function will never throw an exception." It's an optional +// optimization tool, but we may need to use it to match glibc prototypes. +#ifndef __THROW // I guess we're not on a glibc system +# define __THROW // __THROW is just an optimization, so ok to make it "" +#endif + +//------------------------------------------------------------------- +// Configuration +//------------------------------------------------------------------- + +// Not all possible combinations of the following parameters make +// sense. In particular, if kMaxSize increases, you may have to +// increase kNumClasses as well. +static const size_t kPageShift = 12; +static const size_t kPageSize = 1 << kPageShift; +static const size_t kMaxSize = 8u * kPageSize; +static const size_t kAlignShift = 3; +static const size_t kAlignment = 1 << kAlignShift; +static const size_t kNumClasses = 68; + +// Allocates a big block of memory for the pagemap once we reach more than +// 128MB +static const size_t kPageMapBigAllocationThreshold = 128 << 20; + +// Minimum number of pages to fetch from system at a time. Must be +// significantly bigger than kPageSize to amortize system-call +// overhead, and also to reduce external fragementation. Also, we +// should keep this value big because various incarnations of Linux +// have small limits on the number of mmap() regions per +// address-space. +static const size_t kMinSystemAlloc = 1 << (20 - kPageShift); + +// Number of objects to move between a per-thread list and a central +// list in one shot. We want this to be not too small so we can +// amortize the lock overhead for accessing the central list. Making +// it too big may temporarily cause unnecessary memory wastage in the +// per-thread free list until the scavenger cleans up the list. +static int num_objects_to_move[kNumClasses]; + +// Maximum length we allow a per-thread free-list to have before we +// move objects from it into the corresponding central free-list. We +// want this big to avoid locking the central free-list too often. It +// should not hurt to make this list somewhat big because the +// scavenging code will shrink it down when its contents are not in use. +static const int kMaxFreeListLength = 256; + +// Lower and upper bounds on the per-thread cache sizes +static const size_t kMinThreadCacheSize = kMaxSize * 2; +#if PLATFORM(IOS) +static const size_t kMaxThreadCacheSize = 512 * 1024; +#else +static const size_t kMaxThreadCacheSize = 2 << 20; +#endif + +// Default bound on the total amount of thread caches +static const size_t kDefaultOverallThreadCacheSize = 16 << 20; + +// For all span-lengths < kMaxPages we keep an exact-size list. +// REQUIRED: kMaxPages >= kMinSystemAlloc; +static const size_t kMaxPages = kMinSystemAlloc; + +/* The smallest prime > 2^n */ +static int primes_list[] = { + // Small values might cause high rates of sampling + // and hence commented out. + // 2, 5, 11, 17, 37, 67, 131, 257, + // 521, 1031, 2053, 4099, 8209, 16411, + 32771, 65537, 131101, 262147, 524309, 1048583, + 2097169, 4194319, 8388617, 16777259, 33554467 }; + +// Twice the approximate gap between sampling actions. +// I.e., we take one sample approximately once every +// tcmalloc_sample_parameter/2 +// bytes of allocation, i.e., ~ once every 128KB. +// Must be a prime number. +#ifdef NO_TCMALLOC_SAMPLES +DEFINE_int64(tcmalloc_sample_parameter, 0, + "Unused: code is compiled with NO_TCMALLOC_SAMPLES"); +static size_t sample_period = 0; +#else +DEFINE_int64(tcmalloc_sample_parameter, 262147, + "Twice the approximate gap between sampling actions." + " Must be a prime number. Otherwise will be rounded up to a " + " larger prime number"); +static size_t sample_period = 262147; +#endif + +// Protects sample_period above +static SpinLock sample_period_lock = SPINLOCK_INITIALIZER; + +// Parameters for controlling how fast memory is returned to the OS. + +DEFINE_double(tcmalloc_release_rate, 1, + "Rate at which we release unused memory to the system. " + "Zero means we never release memory back to the system. " + "Increase this flag to return memory faster; decrease it " + "to return memory slower. Reasonable rates are in the " + "range [0,10]"); + +//------------------------------------------------------------------- +// Mapping from size to size_class and vice versa +//------------------------------------------------------------------- + +// Sizes <= 1024 have an alignment >= 8. So for such sizes we have an +// array indexed by ceil(size/8). Sizes > 1024 have an alignment >= 128. +// So for these larger sizes we have an array indexed by ceil(size/128). +// +// We flatten both logical arrays into one physical array and use +// arithmetic to compute an appropriate index. The constants used by +// ClassIndex() were selected to make the flattening work. +// +// Examples: +// Size Expression Index +// ------------------------------------------------------- +// 0 (0 + 7) / 8 0 +// 1 (1 + 7) / 8 1 +// ... +// 1024 (1024 + 7) / 8 128 +// 1025 (1025 + 127 + (120<<7)) / 128 129 +// ... +// 32768 (32768 + 127 + (120<<7)) / 128 376 +static const size_t kMaxSmallSize = 1024; +static const int shift_amount[2] = { 3, 7 }; // For divides by 8 or 128 +static const int add_amount[2] = { 7, 127 + (120 << 7) }; +static unsigned char class_array[377]; + +// Compute index of the class_array[] entry for a given size +static inline int ClassIndex(size_t s) { + const int i = (s > kMaxSmallSize); + return static_cast<int>((s + add_amount[i]) >> shift_amount[i]); +} + +// Mapping from size class to max size storable in that class +static size_t class_to_size[kNumClasses]; + +// Mapping from size class to number of pages to allocate at a time +static size_t class_to_pages[kNumClasses]; + +// TransferCache is used to cache transfers of num_objects_to_move[size_class] +// back and forth between thread caches and the central cache for a given size +// class. +struct TCEntry { + void *head; // Head of chain of objects. + void *tail; // Tail of chain of objects. +}; +// A central cache freelist can have anywhere from 0 to kNumTransferEntries +// slots to put link list chains into. To keep memory usage bounded the total +// number of TCEntries across size classes is fixed. Currently each size +// class is initially given one TCEntry which also means that the maximum any +// one class can have is kNumClasses. +static const int kNumTransferEntries = kNumClasses; + +// Note: the following only works for "n"s that fit in 32-bits, but +// that is fine since we only use it for small sizes. +static inline int LgFloor(size_t n) { + int log = 0; + for (int i = 4; i >= 0; --i) { + int shift = (1 << i); + size_t x = n >> shift; + if (x != 0) { + n = x; + log += shift; + } + } + ASSERT(n == 1); + return log; +} + +// Some very basic linked list functions for dealing with using void * as +// storage. + +static inline void *SLL_Next(void *t) { + return *(reinterpret_cast<void**>(t)); +} + +static inline void SLL_SetNext(void *t, void *n) { + *(reinterpret_cast<void**>(t)) = n; +} + +static inline void SLL_Push(void **list, void *element) { + SLL_SetNext(element, *list); + *list = element; +} + +static inline void *SLL_Pop(void **list) { + void *result = *list; + *list = SLL_Next(*list); + return result; +} + + +// Remove N elements from a linked list to which head points. head will be +// modified to point to the new head. start and end will point to the first +// and last nodes of the range. Note that end will point to NULL after this +// function is called. +static inline void SLL_PopRange(void **head, int N, void **start, void **end) { + if (N == 0) { + *start = NULL; + *end = NULL; + return; + } + + void *tmp = *head; + for (int i = 1; i < N; ++i) { + tmp = SLL_Next(tmp); + } + + *start = *head; + *end = tmp; + *head = SLL_Next(tmp); + // Unlink range from list. + SLL_SetNext(tmp, NULL); +} + +static inline void SLL_PushRange(void **head, void *start, void *end) { + if (!start) return; + SLL_SetNext(end, *head); + *head = start; +} + +static inline size_t SLL_Size(void *head) { + int count = 0; + while (head) { + count++; + head = SLL_Next(head); + } + return count; +} + +// Setup helper functions. + +static ALWAYS_INLINE size_t SizeClass(size_t size) { + return class_array[ClassIndex(size)]; +} + +// Get the byte-size for a specified class +static ALWAYS_INLINE size_t ByteSizeForClass(size_t cl) { + return class_to_size[cl]; +} +static int NumMoveSize(size_t size) { + if (size == 0) return 0; + // Use approx 64k transfers between thread and central caches. + int num = static_cast<int>(64.0 * 1024.0 / size); + if (num < 2) num = 2; + // Clamp well below kMaxFreeListLength to avoid ping pong between central + // and thread caches. + if (num > static_cast<int>(0.8 * kMaxFreeListLength)) + num = static_cast<int>(0.8 * kMaxFreeListLength); + + // Also, avoid bringing in too many objects into small object free + // lists. There are lots of such lists, and if we allow each one to + // fetch too many at a time, we end up having to scavenge too often + // (especially when there are lots of threads and each thread gets a + // small allowance for its thread cache). + // + // TODO: Make thread cache free list sizes dynamic so that we do not + // have to equally divide a fixed resource amongst lots of threads. + if (num > 32) num = 32; + + return num; +} + +// Initialize the mapping arrays +static void InitSizeClasses() { + // Do some sanity checking on add_amount[]/shift_amount[]/class_array[] + if (ClassIndex(0) < 0) { + MESSAGE("Invalid class index %d for size 0\n", ClassIndex(0)); + CRASH(); + } + if (static_cast<size_t>(ClassIndex(kMaxSize)) >= sizeof(class_array)) { + MESSAGE("Invalid class index %d for kMaxSize\n", ClassIndex(kMaxSize)); + CRASH(); + } + + // Compute the size classes we want to use + size_t sc = 1; // Next size class to assign + unsigned char alignshift = kAlignShift; + int last_lg = -1; + for (size_t size = kAlignment; size <= kMaxSize; size += (1 << alignshift)) { + int lg = LgFloor(size); + if (lg > last_lg) { + // Increase alignment every so often. + // + // Since we double the alignment every time size doubles and + // size >= 128, this means that space wasted due to alignment is + // at most 16/128 i.e., 12.5%. Plus we cap the alignment at 256 + // bytes, so the space wasted as a percentage starts falling for + // sizes > 2K. + if ((lg >= 7) && (alignshift < 8)) { + alignshift++; + } + last_lg = lg; + } + + // Allocate enough pages so leftover is less than 1/8 of total. + // This bounds wasted space to at most 12.5%. + size_t psize = kPageSize; + while ((psize % size) > (psize >> 3)) { + psize += kPageSize; + } + const size_t my_pages = psize >> kPageShift; + + if (sc > 1 && my_pages == class_to_pages[sc-1]) { + // See if we can merge this into the previous class without + // increasing the fragmentation of the previous class. + const size_t my_objects = (my_pages << kPageShift) / size; + const size_t prev_objects = (class_to_pages[sc-1] << kPageShift) + / class_to_size[sc-1]; + if (my_objects == prev_objects) { + // Adjust last class to include this size + class_to_size[sc-1] = size; + continue; + } + } + + // Add new class + class_to_pages[sc] = my_pages; + class_to_size[sc] = size; + sc++; + } + if (sc != kNumClasses) { + MESSAGE("wrong number of size classes: found %" PRIuS " instead of %d\n", + sc, int(kNumClasses)); + CRASH(); + } + + // Initialize the mapping arrays + int next_size = 0; + for (unsigned char c = 1; c < kNumClasses; c++) { + const size_t max_size_in_class = class_to_size[c]; + for (size_t s = next_size; s <= max_size_in_class; s += kAlignment) { + class_array[ClassIndex(s)] = c; + } + next_size = static_cast<int>(max_size_in_class + kAlignment); + } + + // Double-check sizes just to be safe + for (size_t size = 0; size <= kMaxSize; size++) { + const size_t sc = SizeClass(size); + if (sc == 0) { + MESSAGE("Bad size class %" PRIuS " for %" PRIuS "\n", sc, size); + CRASH(); + } + if (sc > 1 && size <= class_to_size[sc-1]) { + MESSAGE("Allocating unnecessarily large class %" PRIuS " for %" PRIuS + "\n", sc, size); + CRASH(); + } + if (sc >= kNumClasses) { + MESSAGE("Bad size class %" PRIuS " for %" PRIuS "\n", sc, size); + CRASH(); + } + const size_t s = class_to_size[sc]; + if (size > s) { + MESSAGE("Bad size %" PRIuS " for %" PRIuS " (sc = %" PRIuS ")\n", s, size, sc); + CRASH(); + } + if (s == 0) { + MESSAGE("Bad size %" PRIuS " for %" PRIuS " (sc = %" PRIuS ")\n", s, size, sc); + CRASH(); + } + } + + // Initialize the num_objects_to_move array. + for (size_t cl = 1; cl < kNumClasses; ++cl) { + num_objects_to_move[cl] = NumMoveSize(ByteSizeForClass(cl)); + } + +#ifndef WTF_CHANGES + if (false) { + // Dump class sizes and maximum external wastage per size class + for (size_t cl = 1; cl < kNumClasses; ++cl) { + const int alloc_size = class_to_pages[cl] << kPageShift; + const int alloc_objs = alloc_size / class_to_size[cl]; + const int min_used = (class_to_size[cl-1] + 1) * alloc_objs; + const int max_waste = alloc_size - min_used; + MESSAGE("SC %3d [ %8d .. %8d ] from %8d ; %2.0f%% maxwaste\n", + int(cl), + int(class_to_size[cl-1] + 1), + int(class_to_size[cl]), + int(class_to_pages[cl] << kPageShift), + max_waste * 100.0 / alloc_size + ); + } + } +#endif +} + +// ------------------------------------------------------------------------- +// Simple allocator for objects of a specified type. External locking +// is required before accessing one of these objects. +// ------------------------------------------------------------------------- + +// Metadata allocator -- keeps stats about how many bytes allocated +static uint64_t metadata_system_bytes = 0; +static void* MetaDataAlloc(size_t bytes) { + void* result = TCMalloc_SystemAlloc(bytes, 0); + if (result != NULL) { + metadata_system_bytes += bytes; + } + return result; +} + +template <class T> +class PageHeapAllocator { + private: + // How much to allocate from system at a time + static const size_t kAllocIncrement = 32 << 10; + + // Aligned size of T + static const size_t kAlignedSize + = (((sizeof(T) + kAlignment - 1) / kAlignment) * kAlignment); + + // Free area from which to carve new objects + char* free_area_; + size_t free_avail_; + + // Linked list of all regions allocated by this allocator + void* allocated_regions_; + + // Free list of already carved objects + void* free_list_; + + // Number of allocated but unfreed objects + int inuse_; + + public: + void Init() { + ASSERT(kAlignedSize <= kAllocIncrement); + inuse_ = 0; + allocated_regions_ = 0; + free_area_ = NULL; + free_avail_ = 0; + free_list_ = NULL; + } + + T* New() { + // Consult free list + void* result; + if (free_list_ != NULL) { + result = free_list_; + free_list_ = *(reinterpret_cast<void**>(result)); + } else { + if (free_avail_ < kAlignedSize) { + // Need more room + char* new_allocation = reinterpret_cast<char*>(MetaDataAlloc(kAllocIncrement)); + if (!new_allocation) + CRASH(); + + *reinterpret_cast_ptr<void**>(new_allocation) = allocated_regions_; + allocated_regions_ = new_allocation; + free_area_ = new_allocation + kAlignedSize; + free_avail_ = kAllocIncrement - kAlignedSize; + } + result = free_area_; + free_area_ += kAlignedSize; + free_avail_ -= kAlignedSize; + } + inuse_++; + return reinterpret_cast<T*>(result); + } + + void Delete(T* p) { + *(reinterpret_cast<void**>(p)) = free_list_; + free_list_ = p; + inuse_--; + } + + int inuse() const { return inuse_; } + +#if defined(WTF_CHANGES) && OS(DARWIN) + template <class Recorder> + void recordAdministrativeRegions(Recorder& recorder, const RemoteMemoryReader& reader) + { + for (void* adminAllocation = allocated_regions_; adminAllocation; adminAllocation = reader.nextEntryInLinkedList(reinterpret_cast<void**>(adminAllocation))) + recorder.recordRegion(reinterpret_cast<vm_address_t>(adminAllocation), kAllocIncrement); + } +#endif +}; + +// ------------------------------------------------------------------------- +// Span - a contiguous run of pages +// ------------------------------------------------------------------------- + +// Type that can hold a page number +typedef uintptr_t PageID; + +// Type that can hold the length of a run of pages +typedef uintptr_t Length; + +static const Length kMaxValidPages = (~static_cast<Length>(0)) >> kPageShift; + +// Convert byte size into pages. This won't overflow, but may return +// an unreasonably large value if bytes is huge enough. +static inline Length pages(size_t bytes) { + return (bytes >> kPageShift) + + ((bytes & (kPageSize - 1)) > 0 ? 1 : 0); +} + +// Convert a user size into the number of bytes that will actually be +// allocated +static size_t AllocationSize(size_t bytes) { + if (bytes > kMaxSize) { + // Large object: we allocate an integral number of pages + ASSERT(bytes <= (kMaxValidPages << kPageShift)); + return pages(bytes) << kPageShift; + } else { + // Small object: find the size class to which it belongs + return ByteSizeForClass(SizeClass(bytes)); + } +} + +// Information kept for a span (a contiguous run of pages). +struct Span { + PageID start; // Starting page number + Length length; // Number of pages in span + Span* next; // Used when in link list + Span* prev; // Used when in link list + void* objects; // Linked list of free objects + unsigned int free : 1; // Is the span free +#ifndef NO_TCMALLOC_SAMPLES + unsigned int sample : 1; // Sampled object? +#endif + unsigned int sizeclass : 8; // Size-class for small objects (or 0) + unsigned int refcount : 11; // Number of non-free objects + bool decommitted : 1; + +#undef SPAN_HISTORY +#ifdef SPAN_HISTORY + // For debugging, we can keep a log events per span + int nexthistory; + char history[64]; + int value[64]; +#endif +}; + +#define ASSERT_SPAN_COMMITTED(span) ASSERT(!span->decommitted) + +#ifdef SPAN_HISTORY +void Event(Span* span, char op, int v = 0) { + span->history[span->nexthistory] = op; + span->value[span->nexthistory] = v; + span->nexthistory++; + if (span->nexthistory == sizeof(span->history)) span->nexthistory = 0; +} +#else +#define Event(s,o,v) ((void) 0) +#endif + +// Allocator/deallocator for spans +static PageHeapAllocator<Span> span_allocator; +static Span* NewSpan(PageID p, Length len) { + Span* result = span_allocator.New(); + memset(result, 0, sizeof(*result)); + result->start = p; + result->length = len; +#ifdef SPAN_HISTORY + result->nexthistory = 0; +#endif + return result; +} + +static inline void DeleteSpan(Span* span) { +#ifndef NDEBUG + // In debug mode, trash the contents of deleted Spans + memset(span, 0x3f, sizeof(*span)); +#endif + span_allocator.Delete(span); +} + +// ------------------------------------------------------------------------- +// Doubly linked list of spans. +// ------------------------------------------------------------------------- + +static inline void DLL_Init(Span* list) { + list->next = list; + list->prev = list; +} + +static inline void DLL_Remove(Span* span) { + span->prev->next = span->next; + span->next->prev = span->prev; + span->prev = NULL; + span->next = NULL; +} + +static ALWAYS_INLINE bool DLL_IsEmpty(const Span* list) { + return list->next == list; +} + +static int DLL_Length(const Span* list) { + int result = 0; + for (Span* s = list->next; s != list; s = s->next) { + result++; + } + return result; +} + +#if 0 /* Not needed at the moment -- causes compiler warnings if not used */ +static void DLL_Print(const char* label, const Span* list) { + MESSAGE("%-10s %p:", label, list); + for (const Span* s = list->next; s != list; s = s->next) { + MESSAGE(" <%p,%u,%u>", s, s->start, s->length); + } + MESSAGE("\n"); +} +#endif + +static inline void DLL_Prepend(Span* list, Span* span) { + ASSERT(span->next == NULL); + ASSERT(span->prev == NULL); + span->next = list->next; + span->prev = list; + list->next->prev = span; + list->next = span; +} + +// ------------------------------------------------------------------------- +// Stack traces kept for sampled allocations +// The following state is protected by pageheap_lock_. +// ------------------------------------------------------------------------- + +// size/depth are made the same size as a pointer so that some generic +// code below can conveniently cast them back and forth to void*. +static const int kMaxStackDepth = 31; +struct StackTrace { + uintptr_t size; // Size of object + uintptr_t depth; // Number of PC values stored in array below + void* stack[kMaxStackDepth]; +}; +static PageHeapAllocator<StackTrace> stacktrace_allocator; +static Span sampled_objects; + +// ------------------------------------------------------------------------- +// Map from page-id to per-page data +// ------------------------------------------------------------------------- + +// We use PageMap2<> for 32-bit and PageMap3<> for 64-bit machines. +// We also use a simple one-level cache for hot PageID-to-sizeclass mappings, +// because sometimes the sizeclass is all the information we need. + +// Selector class -- general selector uses 3-level map +template <int BITS> class MapSelector { + public: + typedef TCMalloc_PageMap3<BITS-kPageShift> Type; + typedef PackedCache<BITS, uint64_t> CacheType; +}; + +#if defined(WTF_CHANGES) +#if CPU(X86_64) +// On all known X86-64 platforms, the upper 16 bits are always unused and therefore +// can be excluded from the PageMap key. +// See http://en.wikipedia.org/wiki/X86-64#Virtual_address_space_details + +static const size_t kBitsUnusedOn64Bit = 16; +#else +static const size_t kBitsUnusedOn64Bit = 0; +#endif + +// A three-level map for 64-bit machines +template <> class MapSelector<64> { + public: + typedef TCMalloc_PageMap3<64 - kPageShift - kBitsUnusedOn64Bit> Type; + typedef PackedCache<64, uint64_t> CacheType; +}; +#endif + +// A two-level map for 32-bit machines +template <> class MapSelector<32> { + public: + typedef TCMalloc_PageMap2<32 - kPageShift> Type; + typedef PackedCache<32 - kPageShift, uint16_t> CacheType; +}; + +// ------------------------------------------------------------------------- +// Page-level allocator +// * Eager coalescing +// +// Heap for page-level allocation. We allow allocating and freeing a +// contiguous runs of pages (called a "span"). +// ------------------------------------------------------------------------- + +#if USE_BACKGROUND_THREAD_TO_SCAVENGE_MEMORY +// The page heap maintains a free list for spans that are no longer in use by +// the central cache or any thread caches. We use a background thread to +// periodically scan the free list and release a percentage of it back to the OS. + +// If free_committed_pages_ exceeds kMinimumFreeCommittedPageCount, the +// background thread: +// - wakes up +// - pauses for kScavengeDelayInSeconds +// - returns to the OS a percentage of the memory that remained unused during +// that pause (kScavengePercentage * min_free_committed_pages_since_last_scavenge_) +// The goal of this strategy is to reduce memory pressure in a timely fashion +// while avoiding thrashing the OS allocator. + +// Time delay before the page heap scavenger will consider returning pages to +// the OS. +static const int kScavengeDelayInSeconds = 2; + +// Approximate percentage of free committed pages to return to the OS in one +// scavenge. +static const float kScavengePercentage = .5f; + +// number of span lists to keep spans in when memory is returned. +static const int kMinSpanListsWithSpans = 32; + +// Number of free committed pages that we want to keep around. The minimum number of pages used when there +// is 1 span in each of the first kMinSpanListsWithSpans spanlists. Currently 528 pages. +static const size_t kMinimumFreeCommittedPageCount = kMinSpanListsWithSpans * ((1.0f+kMinSpanListsWithSpans) / 2.0f); + +#endif + +static SpinLock pageheap_lock = SPINLOCK_INITIALIZER; + +class TCMalloc_PageHeap { + public: + void init(); + + // Allocate a run of "n" pages. Returns zero if out of memory. + Span* New(Length n); + + // Delete the span "[p, p+n-1]". + // REQUIRES: span was returned by earlier call to New() and + // has not yet been deleted. + void Delete(Span* span); + + // Mark an allocated span as being used for small objects of the + // specified size-class. + // REQUIRES: span was returned by an earlier call to New() + // and has not yet been deleted. + void RegisterSizeClass(Span* span, size_t sc); + + // Split an allocated span into two spans: one of length "n" pages + // followed by another span of length "span->length - n" pages. + // Modifies "*span" to point to the first span of length "n" pages. + // Returns a pointer to the second span. + // + // REQUIRES: "0 < n < span->length" + // REQUIRES: !span->free + // REQUIRES: span->sizeclass == 0 + Span* Split(Span* span, Length n); + + // Return the descriptor for the specified page. + inline Span* GetDescriptor(PageID p) const { + return reinterpret_cast<Span*>(pagemap_.get(p)); + } + +#ifdef WTF_CHANGES + inline Span* GetDescriptorEnsureSafe(PageID p) + { + pagemap_.Ensure(p, 1); + return GetDescriptor(p); + } + + size_t ReturnedBytes() const; +#endif + + // Dump state to stderr +#ifndef WTF_CHANGES + void Dump(TCMalloc_Printer* out); +#endif + + // Return number of bytes allocated from system + inline uint64_t SystemBytes() const { return system_bytes_; } + + // Return number of free bytes in heap + uint64_t FreeBytes() const { + return (static_cast<uint64_t>(free_pages_) << kPageShift); + } + + bool Check(); + size_t CheckList(Span* list, Length min_pages, Length max_pages, bool decommitted); + + // Release all pages on the free list for reuse by the OS: + void ReleaseFreePages(); + void ReleaseFreeList(Span*, Span*); + + // Return 0 if we have no information, or else the correct sizeclass for p. + // Reads and writes to pagemap_cache_ do not require locking. + // The entries are 64 bits on 64-bit hardware and 16 bits on + // 32-bit hardware, and we don't mind raciness as long as each read of + // an entry yields a valid entry, not a partially updated entry. + size_t GetSizeClassIfCached(PageID p) const { + return pagemap_cache_.GetOrDefault(p, 0); + } + void CacheSizeClass(PageID p, size_t cl) const { pagemap_cache_.Put(p, cl); } + + private: + // Pick the appropriate map and cache types based on pointer size + typedef MapSelector<8*sizeof(uintptr_t)>::Type PageMap; + typedef MapSelector<8*sizeof(uintptr_t)>::CacheType PageMapCache; + PageMap pagemap_; + mutable PageMapCache pagemap_cache_; + + // We segregate spans of a given size into two circular linked + // lists: one for normal spans, and one for spans whose memory + // has been returned to the system. + struct SpanList { + Span normal; + Span returned; + }; + + // List of free spans of length >= kMaxPages + SpanList large_; + + // Array mapping from span length to a doubly linked list of free spans + SpanList free_[kMaxPages]; + + // Number of pages kept in free lists + uintptr_t free_pages_; + + // Bytes allocated from system + uint64_t system_bytes_; + +#if USE_BACKGROUND_THREAD_TO_SCAVENGE_MEMORY + // Number of pages kept in free lists that are still committed. + Length free_committed_pages_; + + // Minimum number of free committed pages since last scavenge. (Can be 0 if + // we've committed new pages since the last scavenge.) + Length min_free_committed_pages_since_last_scavenge_; +#endif + + bool GrowHeap(Length n); + + // REQUIRES span->length >= n + // Remove span from its free list, and move any leftover part of + // span into appropriate free lists. Also update "span" to have + // length exactly "n" and mark it as non-free so it can be returned + // to the client. + // + // "released" is true iff "span" was found on a "returned" list. + void Carve(Span* span, Length n, bool released); + + void RecordSpan(Span* span) { + pagemap_.set(span->start, span); + if (span->length > 1) { + pagemap_.set(span->start + span->length - 1, span); + } + } + + // Allocate a large span of length == n. If successful, returns a + // span of exactly the specified length. Else, returns NULL. + Span* AllocLarge(Length n); + +#if !USE_BACKGROUND_THREAD_TO_SCAVENGE_MEMORY + // Incrementally release some memory to the system. + // IncrementalScavenge(n) is called whenever n pages are freed. + void IncrementalScavenge(Length n); +#endif + + // Number of pages to deallocate before doing more scavenging + int64_t scavenge_counter_; + + // Index of last free list we scavenged + size_t scavenge_index_; + +#if defined(WTF_CHANGES) && OS(DARWIN) + friend class FastMallocZone; +#endif + +#if USE_BACKGROUND_THREAD_TO_SCAVENGE_MEMORY + void initializeScavenger(); + ALWAYS_INLINE void signalScavenger(); + void scavenge(); + ALWAYS_INLINE bool shouldScavenge() const; + +#if HAVE(DISPATCH_H) || OS(WINDOWS) + void periodicScavenge(); + ALWAYS_INLINE bool isScavengerSuspended(); + ALWAYS_INLINE void scheduleScavenger(); + ALWAYS_INLINE void rescheduleScavenger(); + ALWAYS_INLINE void suspendScavenger(); +#endif + +#if HAVE(DISPATCH_H) + dispatch_queue_t m_scavengeQueue; + dispatch_source_t m_scavengeTimer; + bool m_scavengingSuspended; +#elif OS(WINDOWS) + static void CALLBACK scavengerTimerFired(void*, BOOLEAN); + HANDLE m_scavengeQueueTimer; +#else + static NO_RETURN_WITH_VALUE void* runScavengerThread(void*); + NO_RETURN void scavengerThread(); + + // Keeps track of whether the background thread is actively scavenging memory every kScavengeDelayInSeconds, or + // it's blocked waiting for more pages to be deleted. + bool m_scavengeThreadActive; + + pthread_mutex_t m_scavengeMutex; + pthread_cond_t m_scavengeCondition; +#endif + +#endif // USE_BACKGROUND_THREAD_TO_SCAVENGE_MEMORY +}; + +void TCMalloc_PageHeap::init() +{ + pagemap_.init(MetaDataAlloc); + pagemap_cache_ = PageMapCache(0); + free_pages_ = 0; + system_bytes_ = 0; + +#if USE_BACKGROUND_THREAD_TO_SCAVENGE_MEMORY + free_committed_pages_ = 0; + min_free_committed_pages_since_last_scavenge_ = 0; +#endif // USE_BACKGROUND_THREAD_TO_SCAVENGE_MEMORY + + scavenge_counter_ = 0; + // Start scavenging at kMaxPages list + scavenge_index_ = kMaxPages-1; + COMPILE_ASSERT(kNumClasses <= (1 << PageMapCache::kValuebits), valuebits); + DLL_Init(&large_.normal); + DLL_Init(&large_.returned); + for (size_t i = 0; i < kMaxPages; i++) { + DLL_Init(&free_[i].normal); + DLL_Init(&free_[i].returned); + } + +#if USE_BACKGROUND_THREAD_TO_SCAVENGE_MEMORY + initializeScavenger(); +#endif // USE_BACKGROUND_THREAD_TO_SCAVENGE_MEMORY +} + +#if USE_BACKGROUND_THREAD_TO_SCAVENGE_MEMORY + +#if HAVE(DISPATCH_H) + +void TCMalloc_PageHeap::initializeScavenger() +{ + m_scavengeQueue = dispatch_queue_create("com.apple.JavaScriptCore.FastMallocSavenger", NULL); + m_scavengeTimer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, m_scavengeQueue); + dispatch_time_t startTime = dispatch_time(DISPATCH_TIME_NOW, kScavengeDelayInSeconds * NSEC_PER_SEC); + dispatch_source_set_timer(m_scavengeTimer, startTime, kScavengeDelayInSeconds * NSEC_PER_SEC, 1000 * NSEC_PER_USEC); + dispatch_source_set_event_handler(m_scavengeTimer, ^{ periodicScavenge(); }); + m_scavengingSuspended = true; +} + +ALWAYS_INLINE bool TCMalloc_PageHeap::isScavengerSuspended() +{ + ASSERT(pageheap_lock.IsHeld()); + return m_scavengingSuspended; +} + +ALWAYS_INLINE void TCMalloc_PageHeap::scheduleScavenger() +{ + ASSERT(pageheap_lock.IsHeld()); + m_scavengingSuspended = false; + dispatch_resume(m_scavengeTimer); +} + +ALWAYS_INLINE void TCMalloc_PageHeap::rescheduleScavenger() +{ + // Nothing to do here for libdispatch. +} + +ALWAYS_INLINE void TCMalloc_PageHeap::suspendScavenger() +{ + ASSERT(pageheap_lock.IsHeld()); + m_scavengingSuspended = true; + dispatch_suspend(m_scavengeTimer); +} + +#elif OS(WINDOWS) + +void TCMalloc_PageHeap::scavengerTimerFired(void* context, BOOLEAN) +{ + static_cast<TCMalloc_PageHeap*>(context)->periodicScavenge(); +} + +void TCMalloc_PageHeap::initializeScavenger() +{ + m_scavengeQueueTimer = 0; +} + +ALWAYS_INLINE bool TCMalloc_PageHeap::isScavengerSuspended() +{ + ASSERT(IsHeld(pageheap_lock)); + return !m_scavengeQueueTimer; +} + +ALWAYS_INLINE void TCMalloc_PageHeap::scheduleScavenger() +{ + // We need to use WT_EXECUTEONLYONCE here and reschedule the timer, because + // Windows will fire the timer event even when the function is already running. + ASSERT(IsHeld(pageheap_lock)); + CreateTimerQueueTimer(&m_scavengeQueueTimer, 0, scavengerTimerFired, this, kScavengeDelayInSeconds * 1000, 0, WT_EXECUTEONLYONCE); +} + +ALWAYS_INLINE void TCMalloc_PageHeap::rescheduleScavenger() +{ + // We must delete the timer and create it again, because it is not possible to retrigger a timer on Windows. + suspendScavenger(); + scheduleScavenger(); +} + +ALWAYS_INLINE void TCMalloc_PageHeap::suspendScavenger() +{ + ASSERT(IsHeld(pageheap_lock)); + HANDLE scavengeQueueTimer = m_scavengeQueueTimer; + m_scavengeQueueTimer = 0; + DeleteTimerQueueTimer(0, scavengeQueueTimer, 0); +} + +#else + +void TCMalloc_PageHeap::initializeScavenger() +{ + // Create a non-recursive mutex. +#if !defined(PTHREAD_MUTEX_NORMAL) || PTHREAD_MUTEX_NORMAL == PTHREAD_MUTEX_DEFAULT + pthread_mutex_init(&m_scavengeMutex, 0); +#else + pthread_mutexattr_t attr; + pthread_mutexattr_init(&attr); + pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_NORMAL); + + pthread_mutex_init(&m_scavengeMutex, &attr); + + pthread_mutexattr_destroy(&attr); +#endif + + pthread_cond_init(&m_scavengeCondition, 0); + m_scavengeThreadActive = true; + pthread_t thread; + pthread_create(&thread, 0, runScavengerThread, this); +} + +void* TCMalloc_PageHeap::runScavengerThread(void* context) +{ + static_cast<TCMalloc_PageHeap*>(context)->scavengerThread(); +#if (COMPILER(MSVC) || COMPILER(SUNCC)) + // Without this, Visual Studio and Sun Studio will complain that this method does not return a value. + return 0; +#endif +} + +ALWAYS_INLINE void TCMalloc_PageHeap::signalScavenger() +{ + // m_scavengeMutex should be held before accessing m_scavengeThreadActive. + ASSERT(pthread_mutex_trylock(m_scavengeMutex)); + if (!m_scavengeThreadActive && shouldScavenge()) + pthread_cond_signal(&m_scavengeCondition); +} + +#endif + +void TCMalloc_PageHeap::scavenge() +{ + size_t pagesToRelease = min_free_committed_pages_since_last_scavenge_ * kScavengePercentage; + size_t targetPageCount = std::max<size_t>(kMinimumFreeCommittedPageCount, free_committed_pages_ - pagesToRelease); + + Length lastFreeCommittedPages = free_committed_pages_; + while (free_committed_pages_ > targetPageCount) { + ASSERT(Check()); + for (int i = kMaxPages; i > 0 && free_committed_pages_ >= targetPageCount; i--) { + SpanList* slist = (static_cast<size_t>(i) == kMaxPages) ? &large_ : &free_[i]; + // If the span size is bigger than kMinSpanListsWithSpans pages return all the spans in the list, else return all but 1 span. + // Return only 50% of a spanlist at a time so spans of size 1 are not the only ones left. + size_t length = DLL_Length(&slist->normal); + size_t numSpansToReturn = (i > kMinSpanListsWithSpans) ? length : length / 2; + for (int j = 0; static_cast<size_t>(j) < numSpansToReturn && !DLL_IsEmpty(&slist->normal) && free_committed_pages_ > targetPageCount; j++) { + Span* s = slist->normal.prev; + DLL_Remove(s); + ASSERT(!s->decommitted); + if (!s->decommitted) { + TCMalloc_SystemRelease(reinterpret_cast<void*>(s->start << kPageShift), + static_cast<size_t>(s->length << kPageShift)); + ASSERT(free_committed_pages_ >= s->length); + free_committed_pages_ -= s->length; + s->decommitted = true; + } + DLL_Prepend(&slist->returned, s); + } + } + + if (lastFreeCommittedPages == free_committed_pages_) + break; + lastFreeCommittedPages = free_committed_pages_; + } + + min_free_committed_pages_since_last_scavenge_ = free_committed_pages_; +} + +ALWAYS_INLINE bool TCMalloc_PageHeap::shouldScavenge() const +{ + return free_committed_pages_ > kMinimumFreeCommittedPageCount; +} + +#endif // USE_BACKGROUND_THREAD_TO_SCAVENGE_MEMORY + +inline Span* TCMalloc_PageHeap::New(Length n) { + ASSERT(Check()); + ASSERT(n > 0); + + // Find first size >= n that has a non-empty list + for (Length s = n; s < kMaxPages; s++) { + Span* ll = NULL; + bool released = false; + if (!DLL_IsEmpty(&free_[s].normal)) { + // Found normal span + ll = &free_[s].normal; + } else if (!DLL_IsEmpty(&free_[s].returned)) { + // Found returned span; reallocate it + ll = &free_[s].returned; + released = true; + } else { + // Keep looking in larger classes + continue; + } + + Span* result = ll->next; + Carve(result, n, released); +#if USE_BACKGROUND_THREAD_TO_SCAVENGE_MEMORY + // The newly allocated memory is from a span that's in the normal span list (already committed). Update the + // free committed pages count. + ASSERT(free_committed_pages_ >= n); + free_committed_pages_ -= n; + if (free_committed_pages_ < min_free_committed_pages_since_last_scavenge_) + min_free_committed_pages_since_last_scavenge_ = free_committed_pages_; +#endif // USE_BACKGROUND_THREAD_TO_SCAVENGE_MEMORY + ASSERT(Check()); + free_pages_ -= n; + return result; + } + + Span* result = AllocLarge(n); + if (result != NULL) { + ASSERT_SPAN_COMMITTED(result); + return result; + } + + // Grow the heap and try again + if (!GrowHeap(n)) { + ASSERT(Check()); + return NULL; + } + + return AllocLarge(n); +} + +Span* TCMalloc_PageHeap::AllocLarge(Length n) { + // find the best span (closest to n in size). + // The following loops implements address-ordered best-fit. + bool from_released = false; + Span *best = NULL; + + // Search through normal list + for (Span* span = large_.normal.next; + span != &large_.normal; + span = span->next) { + if (span->length >= n) { + if ((best == NULL) + || (span->length < best->length) + || ((span->length == best->length) && (span->start < best->start))) { + best = span; + from_released = false; + } + } + } + + // Search through released list in case it has a better fit + for (Span* span = large_.returned.next; + span != &large_.returned; + span = span->next) { + if (span->length >= n) { + if ((best == NULL) + || (span->length < best->length) + || ((span->length == best->length) && (span->start < best->start))) { + best = span; + from_released = true; + } + } + } + + if (best != NULL) { + Carve(best, n, from_released); +#if USE_BACKGROUND_THREAD_TO_SCAVENGE_MEMORY + // The newly allocated memory is from a span that's in the normal span list (already committed). Update the + // free committed pages count. + ASSERT(free_committed_pages_ >= n); + free_committed_pages_ -= n; + if (free_committed_pages_ < min_free_committed_pages_since_last_scavenge_) + min_free_committed_pages_since_last_scavenge_ = free_committed_pages_; +#endif // USE_BACKGROUND_THREAD_TO_SCAVENGE_MEMORY + ASSERT(Check()); + free_pages_ -= n; + return best; + } + return NULL; +} + +Span* TCMalloc_PageHeap::Split(Span* span, Length n) { + ASSERT(0 < n); + ASSERT(n < span->length); + ASSERT(!span->free); + ASSERT(span->sizeclass == 0); + Event(span, 'T', n); + + const Length extra = span->length - n; + Span* leftover = NewSpan(span->start + n, extra); + Event(leftover, 'U', extra); + RecordSpan(leftover); + pagemap_.set(span->start + n - 1, span); // Update map from pageid to span + span->length = n; + + return leftover; +} + +inline void TCMalloc_PageHeap::Carve(Span* span, Length n, bool released) { + ASSERT(n > 0); + DLL_Remove(span); + span->free = 0; + Event(span, 'A', n); + + if (released) { + // If the span chosen to carve from is decommited, commit the entire span at once to avoid committing spans 1 page at a time. + ASSERT(span->decommitted); + TCMalloc_SystemCommit(reinterpret_cast<void*>(span->start << kPageShift), static_cast<size_t>(span->length << kPageShift)); + span->decommitted = false; +#if USE_BACKGROUND_THREAD_TO_SCAVENGE_MEMORY + free_committed_pages_ += span->length; +#endif + } + + const int extra = static_cast<int>(span->length - n); + ASSERT(extra >= 0); + if (extra > 0) { + Span* leftover = NewSpan(span->start + n, extra); + leftover->free = 1; + leftover->decommitted = false; + Event(leftover, 'S', extra); + RecordSpan(leftover); + + // Place leftover span on appropriate free list + SpanList* listpair = (static_cast<size_t>(extra) < kMaxPages) ? &free_[extra] : &large_; + Span* dst = &listpair->normal; + DLL_Prepend(dst, leftover); + + span->length = n; + pagemap_.set(span->start + n - 1, span); + } +} + +static ALWAYS_INLINE void mergeDecommittedStates(Span* destination, Span* other) +{ + if (destination->decommitted && !other->decommitted) { + TCMalloc_SystemRelease(reinterpret_cast<void*>(other->start << kPageShift), + static_cast<size_t>(other->length << kPageShift)); + } else if (other->decommitted && !destination->decommitted) { + TCMalloc_SystemRelease(reinterpret_cast<void*>(destination->start << kPageShift), + static_cast<size_t>(destination->length << kPageShift)); + destination->decommitted = true; + } +} + +inline void TCMalloc_PageHeap::Delete(Span* span) { + ASSERT(Check()); + ASSERT(!span->free); + ASSERT(span->length > 0); + ASSERT(GetDescriptor(span->start) == span); + ASSERT(GetDescriptor(span->start + span->length - 1) == span); + span->sizeclass = 0; +#ifndef NO_TCMALLOC_SAMPLES + span->sample = 0; +#endif + + // Coalesce -- we guarantee that "p" != 0, so no bounds checking + // necessary. We do not bother resetting the stale pagemap + // entries for the pieces we are merging together because we only + // care about the pagemap entries for the boundaries. +#if USE_BACKGROUND_THREAD_TO_SCAVENGE_MEMORY + // Track the total size of the neighboring free spans that are committed. + Length neighboringCommittedSpansLength = 0; +#endif + const PageID p = span->start; + const Length n = span->length; + Span* prev = GetDescriptor(p-1); + if (prev != NULL && prev->free) { + // Merge preceding span into this span + ASSERT(prev->start + prev->length == p); + const Length len = prev->length; +#if USE_BACKGROUND_THREAD_TO_SCAVENGE_MEMORY + if (!prev->decommitted) + neighboringCommittedSpansLength += len; +#endif + mergeDecommittedStates(span, prev); + DLL_Remove(prev); + DeleteSpan(prev); + span->start -= len; + span->length += len; + pagemap_.set(span->start, span); + Event(span, 'L', len); + } + Span* next = GetDescriptor(p+n); + if (next != NULL && next->free) { + // Merge next span into this span + ASSERT(next->start == p+n); + const Length len = next->length; +#if USE_BACKGROUND_THREAD_TO_SCAVENGE_MEMORY + if (!next->decommitted) + neighboringCommittedSpansLength += len; +#endif + mergeDecommittedStates(span, next); + DLL_Remove(next); + DeleteSpan(next); + span->length += len; + pagemap_.set(span->start + span->length - 1, span); + Event(span, 'R', len); + } + + Event(span, 'D', span->length); + span->free = 1; + if (span->decommitted) { + if (span->length < kMaxPages) + DLL_Prepend(&free_[span->length].returned, span); + else + DLL_Prepend(&large_.returned, span); + } else { + if (span->length < kMaxPages) + DLL_Prepend(&free_[span->length].normal, span); + else + DLL_Prepend(&large_.normal, span); + } + free_pages_ += n; + +#if USE_BACKGROUND_THREAD_TO_SCAVENGE_MEMORY + if (span->decommitted) { + // If the merged span is decommitted, that means we decommitted any neighboring spans that were + // committed. Update the free committed pages count. + free_committed_pages_ -= neighboringCommittedSpansLength; + if (free_committed_pages_ < min_free_committed_pages_since_last_scavenge_) + min_free_committed_pages_since_last_scavenge_ = free_committed_pages_; + } else { + // If the merged span remains committed, add the deleted span's size to the free committed pages count. + free_committed_pages_ += n; + } + + // Make sure the scavenge thread becomes active if we have enough freed pages to release some back to the system. + signalScavenger(); +#else + IncrementalScavenge(n); +#endif + + ASSERT(Check()); +} + +#if !USE_BACKGROUND_THREAD_TO_SCAVENGE_MEMORY +void TCMalloc_PageHeap::IncrementalScavenge(Length n) { + // Fast path; not yet time to release memory + scavenge_counter_ -= n; + if (scavenge_counter_ >= 0) return; // Not yet time to scavenge + +#if PLATFORM(IOS) + static const size_t kDefaultReleaseDelay = 64; +#else + // If there is nothing to release, wait for so many pages before + // scavenging again. With 4K pages, this comes to 16MB of memory. + static const size_t kDefaultReleaseDelay = 1 << 8; +#endif + + // Find index of free list to scavenge + size_t index = scavenge_index_ + 1; + for (size_t i = 0; i < kMaxPages+1; i++) { + if (index > kMaxPages) index = 0; + SpanList* slist = (index == kMaxPages) ? &large_ : &free_[index]; + if (!DLL_IsEmpty(&slist->normal)) { + // Release the last span on the normal portion of this list + Span* s = slist->normal.prev; + DLL_Remove(s); + TCMalloc_SystemRelease(reinterpret_cast<void*>(s->start << kPageShift), + static_cast<size_t>(s->length << kPageShift)); + s->decommitted = true; + DLL_Prepend(&slist->returned, s); + +#if PLATFORM(IOS) + scavenge_counter_ = std::max<size_t>(16UL, std::min<size_t>(kDefaultReleaseDelay, kDefaultReleaseDelay - (free_pages_ / kDefaultReleaseDelay))); +#else + scavenge_counter_ = std::max<size_t>(64UL, std::min<size_t>(kDefaultReleaseDelay, kDefaultReleaseDelay - (free_pages_ / kDefaultReleaseDelay))); +#endif + + if (index == kMaxPages && !DLL_IsEmpty(&slist->normal)) + scavenge_index_ = index - 1; + else + scavenge_index_ = index; + return; + } + index++; + } + + // Nothing to scavenge, delay for a while + scavenge_counter_ = kDefaultReleaseDelay; +} +#endif + +void TCMalloc_PageHeap::RegisterSizeClass(Span* span, size_t sc) { + // Associate span object with all interior pages as well + ASSERT(!span->free); + ASSERT(GetDescriptor(span->start) == span); + ASSERT(GetDescriptor(span->start+span->length-1) == span); + Event(span, 'C', sc); + span->sizeclass = static_cast<unsigned int>(sc); + for (Length i = 1; i < span->length-1; i++) { + pagemap_.set(span->start+i, span); + } +} + +#ifdef WTF_CHANGES +size_t TCMalloc_PageHeap::ReturnedBytes() const { + size_t result = 0; + for (unsigned s = 0; s < kMaxPages; s++) { + const int r_length = DLL_Length(&free_[s].returned); + unsigned r_pages = s * r_length; + result += r_pages << kPageShift; + } + + for (Span* s = large_.returned.next; s != &large_.returned; s = s->next) + result += s->length << kPageShift; + return result; +} +#endif + +#ifndef WTF_CHANGES +static double PagesToMB(uint64_t pages) { + return (pages << kPageShift) / 1048576.0; +} + +void TCMalloc_PageHeap::Dump(TCMalloc_Printer* out) { + int nonempty_sizes = 0; + for (int s = 0; s < kMaxPages; s++) { + if (!DLL_IsEmpty(&free_[s].normal) || !DLL_IsEmpty(&free_[s].returned)) { + nonempty_sizes++; + } + } + out->printf("------------------------------------------------\n"); + out->printf("PageHeap: %d sizes; %6.1f MB free\n", + nonempty_sizes, PagesToMB(free_pages_)); + out->printf("------------------------------------------------\n"); + uint64_t total_normal = 0; + uint64_t total_returned = 0; + for (int s = 0; s < kMaxPages; s++) { + const int n_length = DLL_Length(&free_[s].normal); + const int r_length = DLL_Length(&free_[s].returned); + if (n_length + r_length > 0) { + uint64_t n_pages = s * n_length; + uint64_t r_pages = s * r_length; + total_normal += n_pages; + total_returned += r_pages; + out->printf("%6u pages * %6u spans ~ %6.1f MB; %6.1f MB cum" + "; unmapped: %6.1f MB; %6.1f MB cum\n", + s, + (n_length + r_length), + PagesToMB(n_pages + r_pages), + PagesToMB(total_normal + total_returned), + PagesToMB(r_pages), + PagesToMB(total_returned)); + } + } + + uint64_t n_pages = 0; + uint64_t r_pages = 0; + int n_spans = 0; + int r_spans = 0; + out->printf("Normal large spans:\n"); + for (Span* s = large_.normal.next; s != &large_.normal; s = s->next) { + out->printf(" [ %6" PRIuS " pages ] %6.1f MB\n", + s->length, PagesToMB(s->length)); + n_pages += s->length; + n_spans++; + } + out->printf("Unmapped large spans:\n"); + for (Span* s = large_.returned.next; s != &large_.returned; s = s->next) { + out->printf(" [ %6" PRIuS " pages ] %6.1f MB\n", + s->length, PagesToMB(s->length)); + r_pages += s->length; + r_spans++; + } + total_normal += n_pages; + total_returned += r_pages; + out->printf(">255 large * %6u spans ~ %6.1f MB; %6.1f MB cum" + "; unmapped: %6.1f MB; %6.1f MB cum\n", + (n_spans + r_spans), + PagesToMB(n_pages + r_pages), + PagesToMB(total_normal + total_returned), + PagesToMB(r_pages), + PagesToMB(total_returned)); +} +#endif + +bool TCMalloc_PageHeap::GrowHeap(Length n) { + ASSERT(kMaxPages >= kMinSystemAlloc); + if (n > kMaxValidPages) return false; + Length ask = (n>kMinSystemAlloc) ? n : static_cast<Length>(kMinSystemAlloc); + size_t actual_size; + void* ptr = TCMalloc_SystemAlloc(ask << kPageShift, &actual_size, kPageSize); + if (ptr == NULL) { + if (n < ask) { + // Try growing just "n" pages + ask = n; + ptr = TCMalloc_SystemAlloc(ask << kPageShift, &actual_size, kPageSize); + } + if (ptr == NULL) return false; + } + ask = actual_size >> kPageShift; + + uint64_t old_system_bytes = system_bytes_; + system_bytes_ += (ask << kPageShift); + const PageID p = reinterpret_cast<uintptr_t>(ptr) >> kPageShift; + ASSERT(p > 0); + + // If we have already a lot of pages allocated, just pre allocate a bunch of + // memory for the page map. This prevents fragmentation by pagemap metadata + // when a program keeps allocating and freeing large blocks. + + if (old_system_bytes < kPageMapBigAllocationThreshold + && system_bytes_ >= kPageMapBigAllocationThreshold) { + pagemap_.PreallocateMoreMemory(); + } + + // Make sure pagemap_ has entries for all of the new pages. + // Plus ensure one before and one after so coalescing code + // does not need bounds-checking. + if (pagemap_.Ensure(p-1, ask+2)) { + // Pretend the new area is allocated and then Delete() it to + // cause any necessary coalescing to occur. + // + // We do not adjust free_pages_ here since Delete() will do it for us. + Span* span = NewSpan(p, ask); + RecordSpan(span); + Delete(span); + ASSERT(Check()); + return true; + } else { + // We could not allocate memory within "pagemap_" + // TODO: Once we can return memory to the system, return the new span + return false; + } +} + +bool TCMalloc_PageHeap::Check() { +#if USE_BACKGROUND_THREAD_TO_SCAVENGE_MEMORY + size_t totalFreeCommitted = 0; +#endif + ASSERT(free_[0].normal.next == &free_[0].normal); + ASSERT(free_[0].returned.next == &free_[0].returned); +#if USE_BACKGROUND_THREAD_TO_SCAVENGE_MEMORY + totalFreeCommitted = CheckList(&large_.normal, kMaxPages, 1000000000, false); +#else + CheckList(&large_.normal, kMaxPages, 1000000000, false); +#endif + CheckList(&large_.returned, kMaxPages, 1000000000, true); + for (Length s = 1; s < kMaxPages; s++) { +#if USE_BACKGROUND_THREAD_TO_SCAVENGE_MEMORY + totalFreeCommitted += CheckList(&free_[s].normal, s, s, false); +#else + CheckList(&free_[s].normal, s, s, false); +#endif + CheckList(&free_[s].returned, s, s, true); + } +#if USE_BACKGROUND_THREAD_TO_SCAVENGE_MEMORY + ASSERT(totalFreeCommitted == free_committed_pages_); +#endif + return true; +} + +#if ASSERT_DISABLED +size_t TCMalloc_PageHeap::CheckList(Span*, Length, Length, bool) { + return 0; +} +#else +size_t TCMalloc_PageHeap::CheckList(Span* list, Length min_pages, Length max_pages, bool decommitted) { + size_t freeCount = 0; + for (Span* s = list->next; s != list; s = s->next) { + CHECK_CONDITION(s->free); + CHECK_CONDITION(s->length >= min_pages); + CHECK_CONDITION(s->length <= max_pages); + CHECK_CONDITION(GetDescriptor(s->start) == s); + CHECK_CONDITION(GetDescriptor(s->start+s->length-1) == s); + CHECK_CONDITION(s->decommitted == decommitted); + freeCount += s->length; + } + return freeCount; +} +#endif + +void TCMalloc_PageHeap::ReleaseFreeList(Span* list, Span* returned) { + // Walk backwards through list so that when we push these + // spans on the "returned" list, we preserve the order. +#if USE_BACKGROUND_THREAD_TO_SCAVENGE_MEMORY + size_t freePageReduction = 0; +#endif + + while (!DLL_IsEmpty(list)) { + Span* s = list->prev; + + DLL_Remove(s); + s->decommitted = true; + DLL_Prepend(returned, s); + TCMalloc_SystemRelease(reinterpret_cast<void*>(s->start << kPageShift), + static_cast<size_t>(s->length << kPageShift)); +#if USE_BACKGROUND_THREAD_TO_SCAVENGE_MEMORY + freePageReduction += s->length; +#endif + } + +#if USE_BACKGROUND_THREAD_TO_SCAVENGE_MEMORY + free_committed_pages_ -= freePageReduction; + if (free_committed_pages_ < min_free_committed_pages_since_last_scavenge_) + min_free_committed_pages_since_last_scavenge_ = free_committed_pages_; +#endif +} + +void TCMalloc_PageHeap::ReleaseFreePages() { + for (Length s = 0; s < kMaxPages; s++) { + ReleaseFreeList(&free_[s].normal, &free_[s].returned); + } + ReleaseFreeList(&large_.normal, &large_.returned); + ASSERT(Check()); +} + +//------------------------------------------------------------------- +// Free list +//------------------------------------------------------------------- + +class TCMalloc_ThreadCache_FreeList { + private: + void* list_; // Linked list of nodes + uint16_t length_; // Current length + uint16_t lowater_; // Low water mark for list length + + public: + void Init() { + list_ = NULL; + length_ = 0; + lowater_ = 0; + } + + // Return current length of list + int length() const { + return length_; + } + + // Is list empty? + bool empty() const { + return list_ == NULL; + } + + // Low-water mark management + int lowwatermark() const { return lowater_; } + void clear_lowwatermark() { lowater_ = length_; } + + ALWAYS_INLINE void Push(void* ptr) { + SLL_Push(&list_, ptr); + length_++; + } + + void PushRange(int N, void *start, void *end) { + SLL_PushRange(&list_, start, end); + length_ = length_ + static_cast<uint16_t>(N); + } + + void PopRange(int N, void **start, void **end) { + SLL_PopRange(&list_, N, start, end); + ASSERT(length_ >= N); + length_ = length_ - static_cast<uint16_t>(N); + if (length_ < lowater_) lowater_ = length_; + } + + ALWAYS_INLINE void* Pop() { + ASSERT(list_ != NULL); + length_--; + if (length_ < lowater_) lowater_ = length_; + return SLL_Pop(&list_); + } + +#ifdef WTF_CHANGES + template <class Finder, class Reader> + void enumerateFreeObjects(Finder& finder, const Reader& reader) + { + for (void* nextObject = list_; nextObject; nextObject = reader.nextEntryInLinkedList(reinterpret_cast<void**>(nextObject))) + finder.visit(nextObject); + } +#endif +}; + +//------------------------------------------------------------------- +// Data kept per thread +//------------------------------------------------------------------- + +class TCMalloc_ThreadCache { + private: + typedef TCMalloc_ThreadCache_FreeList FreeList; +#if OS(WINDOWS) + typedef DWORD ThreadIdentifier; +#else + typedef pthread_t ThreadIdentifier; +#endif + + size_t size_; // Combined size of data + ThreadIdentifier tid_; // Which thread owns it + bool in_setspecific_; // Called pthread_setspecific? + FreeList list_[kNumClasses]; // Array indexed by size-class + + // We sample allocations, biased by the size of the allocation + uint32_t rnd_; // Cheap random number generator + size_t bytes_until_sample_; // Bytes until we sample next + + // Allocate a new heap. REQUIRES: pageheap_lock is held. + static inline TCMalloc_ThreadCache* NewHeap(ThreadIdentifier tid); + + // Use only as pthread thread-specific destructor function. + static void DestroyThreadCache(void* ptr); + public: + // All ThreadCache objects are kept in a linked list (for stats collection) + TCMalloc_ThreadCache* next_; + TCMalloc_ThreadCache* prev_; + + void Init(ThreadIdentifier tid); + void Cleanup(); + + // Accessors (mostly just for printing stats) + int freelist_length(size_t cl) const { return list_[cl].length(); } + + // Total byte size in cache + size_t Size() const { return size_; } + + ALWAYS_INLINE void* Allocate(size_t size); + void Deallocate(void* ptr, size_t size_class); + + ALWAYS_INLINE void FetchFromCentralCache(size_t cl, size_t allocationSize); + void ReleaseToCentralCache(size_t cl, int N); + void Scavenge(); + void Print() const; + + // Record allocation of "k" bytes. Return true iff allocation + // should be sampled + bool SampleAllocation(size_t k); + + // Pick next sampling point + void PickNextSample(size_t k); + + static void InitModule(); + static void InitTSD(); + static TCMalloc_ThreadCache* GetThreadHeap(); + static TCMalloc_ThreadCache* GetCache(); + static TCMalloc_ThreadCache* GetCacheIfPresent(); + static TCMalloc_ThreadCache* CreateCacheIfNecessary(); + static void DeleteCache(TCMalloc_ThreadCache* heap); + static void BecomeIdle(); + static void RecomputeThreadCacheSize(); + +#ifdef WTF_CHANGES + template <class Finder, class Reader> + void enumerateFreeObjects(Finder& finder, const Reader& reader) + { + for (unsigned sizeClass = 0; sizeClass < kNumClasses; sizeClass++) + list_[sizeClass].enumerateFreeObjects(finder, reader); + } +#endif +}; + +//------------------------------------------------------------------- +// Data kept per size-class in central cache +//------------------------------------------------------------------- + +class TCMalloc_Central_FreeList { + public: + void Init(size_t cl); + + // These methods all do internal locking. + + // Insert the specified range into the central freelist. N is the number of + // elements in the range. + void InsertRange(void *start, void *end, int N); + + // Returns the actual number of fetched elements into N. + void RemoveRange(void **start, void **end, int *N); + + // Returns the number of free objects in cache. + size_t length() { + SpinLockHolder h(&lock_); + return counter_; + } + + // Returns the number of free objects in the transfer cache. + int tc_length() { + SpinLockHolder h(&lock_); + return used_slots_ * num_objects_to_move[size_class_]; + } + +#ifdef WTF_CHANGES + template <class Finder, class Reader> + void enumerateFreeObjects(Finder& finder, const Reader& reader, TCMalloc_Central_FreeList* remoteCentralFreeList) + { + for (Span* span = &empty_; span && span != &empty_; span = (span->next ? reader(span->next) : 0)) + ASSERT(!span->objects); + + ASSERT(!nonempty_.objects); + static const ptrdiff_t nonemptyOffset = reinterpret_cast<const char*>(&nonempty_) - reinterpret_cast<const char*>(this); + + Span* remoteNonempty = reinterpret_cast<Span*>(reinterpret_cast<char*>(remoteCentralFreeList) + nonemptyOffset); + Span* remoteSpan = nonempty_.next; + + for (Span* span = reader(remoteSpan); span && remoteSpan != remoteNonempty; remoteSpan = span->next, span = (span->next ? reader(span->next) : 0)) { + for (void* nextObject = span->objects; nextObject; nextObject = reader.nextEntryInLinkedList(reinterpret_cast<void**>(nextObject))) + finder.visit(nextObject); + } + } +#endif + + private: + // REQUIRES: lock_ is held + // Remove object from cache and return. + // Return NULL if no free entries in cache. + void* FetchFromSpans(); + + // REQUIRES: lock_ is held + // Remove object from cache and return. Fetches + // from pageheap if cache is empty. Only returns + // NULL on allocation failure. + void* FetchFromSpansSafe(); + + // REQUIRES: lock_ is held + // Release a linked list of objects to spans. + // May temporarily release lock_. + void ReleaseListToSpans(void *start); + + // REQUIRES: lock_ is held + // Release an object to spans. + // May temporarily release lock_. + ALWAYS_INLINE void ReleaseToSpans(void* object); + + // REQUIRES: lock_ is held + // Populate cache by fetching from the page heap. + // May temporarily release lock_. + ALWAYS_INLINE void Populate(); + + // REQUIRES: lock is held. + // Tries to make room for a TCEntry. If the cache is full it will try to + // expand it at the cost of some other cache size. Return false if there is + // no space. + bool MakeCacheSpace(); + + // REQUIRES: lock_ for locked_size_class is held. + // Picks a "random" size class to steal TCEntry slot from. In reality it + // just iterates over the sizeclasses but does so without taking a lock. + // Returns true on success. + // May temporarily lock a "random" size class. + static ALWAYS_INLINE bool EvictRandomSizeClass(size_t locked_size_class, bool force); + + // REQUIRES: lock_ is *not* held. + // Tries to shrink the Cache. If force is true it will relase objects to + // spans if it allows it to shrink the cache. Return false if it failed to + // shrink the cache. Decrements cache_size_ on succeess. + // May temporarily take lock_. If it takes lock_, the locked_size_class + // lock is released to the thread from holding two size class locks + // concurrently which could lead to a deadlock. + bool ShrinkCache(int locked_size_class, bool force); + + // This lock protects all the data members. cached_entries and cache_size_ + // may be looked at without holding the lock. + SpinLock lock_; + + // We keep linked lists of empty and non-empty spans. + size_t size_class_; // My size class + Span empty_; // Dummy header for list of empty spans + Span nonempty_; // Dummy header for list of non-empty spans + size_t counter_; // Number of free objects in cache entry + + // Here we reserve space for TCEntry cache slots. Since one size class can + // end up getting all the TCEntries quota in the system we just preallocate + // sufficient number of entries here. + TCEntry tc_slots_[kNumTransferEntries]; + + // Number of currently used cached entries in tc_slots_. This variable is + // updated under a lock but can be read without one. + int32_t used_slots_; + // The current number of slots for this size class. This is an + // adaptive value that is increased if there is lots of traffic + // on a given size class. + int32_t cache_size_; +}; + +// Pad each CentralCache object to multiple of 64 bytes +class TCMalloc_Central_FreeListPadded : public TCMalloc_Central_FreeList { + private: + char pad_[(64 - (sizeof(TCMalloc_Central_FreeList) % 64)) % 64]; +}; + +//------------------------------------------------------------------- +// Global variables +//------------------------------------------------------------------- + +// Central cache -- a collection of free-lists, one per size-class. +// We have a separate lock per free-list to reduce contention. +static TCMalloc_Central_FreeListPadded central_cache[kNumClasses]; + +// Page-level allocator +static AllocAlignmentInteger pageheap_memory[(sizeof(TCMalloc_PageHeap) + sizeof(AllocAlignmentInteger) - 1) / sizeof(AllocAlignmentInteger)]; +static bool phinited = false; + +// Avoid extra level of indirection by making "pageheap" be just an alias +// of pageheap_memory. +typedef union { + void* m_memory; + TCMalloc_PageHeap* m_pageHeap; +} PageHeapUnion; + +static inline TCMalloc_PageHeap* getPageHeap() +{ + PageHeapUnion u = { &pageheap_memory[0] }; + return u.m_pageHeap; +} + +#define pageheap getPageHeap() + +#if USE_BACKGROUND_THREAD_TO_SCAVENGE_MEMORY + +#if HAVE(DISPATCH_H) || OS(WINDOWS) + +void TCMalloc_PageHeap::periodicScavenge() +{ + SpinLockHolder h(&pageheap_lock); + pageheap->scavenge(); + + if (shouldScavenge()) { + rescheduleScavenger(); + return; + } + + suspendScavenger(); +} + +ALWAYS_INLINE void TCMalloc_PageHeap::signalScavenger() +{ + ASSERT(pageheap_lock.IsHeld()); + if (isScavengerSuspended() && shouldScavenge()) + scheduleScavenger(); +} + +#else + +void TCMalloc_PageHeap::scavengerThread() +{ +#if HAVE(PTHREAD_SETNAME_NP) + pthread_setname_np("JavaScriptCore: FastMalloc scavenger"); +#endif + + while (1) { + if (!shouldScavenge()) { + pthread_mutex_lock(&m_scavengeMutex); + m_scavengeThreadActive = false; + // Block until there are enough free committed pages to release back to the system. + pthread_cond_wait(&m_scavengeCondition, &m_scavengeMutex); + m_scavengeThreadActive = true; + pthread_mutex_unlock(&m_scavengeMutex); + } + sleep(kScavengeDelayInSeconds); + { + SpinLockHolder h(&pageheap_lock); + pageheap->scavenge(); + } + } +} + +#endif + +#endif + +// If TLS is available, we also store a copy +// of the per-thread object in a __thread variable +// since __thread variables are faster to read +// than pthread_getspecific(). We still need +// pthread_setspecific() because __thread +// variables provide no way to run cleanup +// code when a thread is destroyed. +#ifdef HAVE_TLS +static __thread TCMalloc_ThreadCache *threadlocal_heap; +#endif +// Thread-specific key. Initialization here is somewhat tricky +// because some Linux startup code invokes malloc() before it +// is in a good enough state to handle pthread_keycreate(). +// Therefore, we use TSD keys only after tsd_inited is set to true. +// Until then, we use a slow path to get the heap object. +static bool tsd_inited = false; +#if USE(PTHREAD_GETSPECIFIC_DIRECT) +static const pthread_key_t heap_key = __PTK_FRAMEWORK_JAVASCRIPTCORE_KEY0; +#else +static pthread_key_t heap_key; +#endif +#if OS(WINDOWS) +DWORD tlsIndex = TLS_OUT_OF_INDEXES; +#endif + +static ALWAYS_INLINE void setThreadHeap(TCMalloc_ThreadCache* heap) +{ +#if USE(PTHREAD_GETSPECIFIC_DIRECT) + // Can't have two libraries both doing this in the same process, + // so check and make this crash right away. + if (pthread_getspecific(heap_key)) + CRASH(); +#endif + + // Still do pthread_setspecific even if there's an alternate form + // of thread-local storage in use, to benefit from the delete callback. + pthread_setspecific(heap_key, heap); + +#if OS(WINDOWS) + TlsSetValue(tlsIndex, heap); +#endif +} + +// Allocator for thread heaps +static PageHeapAllocator<TCMalloc_ThreadCache> threadheap_allocator; + +// Linked list of heap objects. Protected by pageheap_lock. +static TCMalloc_ThreadCache* thread_heaps = NULL; +static int thread_heap_count = 0; + +// Overall thread cache size. Protected by pageheap_lock. +static size_t overall_thread_cache_size = kDefaultOverallThreadCacheSize; + +// Global per-thread cache size. Writes are protected by +// pageheap_lock. Reads are done without any locking, which should be +// fine as long as size_t can be written atomically and we don't place +// invariants between this variable and other pieces of state. +static volatile size_t per_thread_cache_size = kMaxThreadCacheSize; + +//------------------------------------------------------------------- +// Central cache implementation +//------------------------------------------------------------------- + +void TCMalloc_Central_FreeList::Init(size_t cl) { + lock_.Init(); + size_class_ = cl; + DLL_Init(&empty_); + DLL_Init(&nonempty_); + counter_ = 0; + + cache_size_ = 1; + used_slots_ = 0; + ASSERT(cache_size_ <= kNumTransferEntries); +} + +void TCMalloc_Central_FreeList::ReleaseListToSpans(void* start) { + while (start) { + void *next = SLL_Next(start); + ReleaseToSpans(start); + start = next; + } +} + +ALWAYS_INLINE void TCMalloc_Central_FreeList::ReleaseToSpans(void* object) { + const PageID p = reinterpret_cast<uintptr_t>(object) >> kPageShift; + Span* span = pageheap->GetDescriptor(p); + ASSERT(span != NULL); + ASSERT(span->refcount > 0); + + // If span is empty, move it to non-empty list + if (span->objects == NULL) { + DLL_Remove(span); + DLL_Prepend(&nonempty_, span); + Event(span, 'N', 0); + } + + // The following check is expensive, so it is disabled by default + if (false) { + // Check that object does not occur in list + unsigned got = 0; + for (void* p = span->objects; p != NULL; p = *((void**) p)) { + ASSERT(p != object); + got++; + } + ASSERT(got + span->refcount == + (span->length<<kPageShift)/ByteSizeForClass(span->sizeclass)); + } + + counter_++; + span->refcount--; + if (span->refcount == 0) { + Event(span, '#', 0); + counter_ -= (span->length<<kPageShift) / ByteSizeForClass(span->sizeclass); + DLL_Remove(span); + + // Release central list lock while operating on pageheap + lock_.Unlock(); + { + SpinLockHolder h(&pageheap_lock); + pageheap->Delete(span); + } + lock_.Lock(); + } else { + *(reinterpret_cast<void**>(object)) = span->objects; + span->objects = object; + } +} + +ALWAYS_INLINE bool TCMalloc_Central_FreeList::EvictRandomSizeClass( + size_t locked_size_class, bool force) { + static int race_counter = 0; + int t = race_counter++; // Updated without a lock, but who cares. + if (t >= static_cast<int>(kNumClasses)) { + while (t >= static_cast<int>(kNumClasses)) { + t -= kNumClasses; + } + race_counter = t; + } + ASSERT(t >= 0); + ASSERT(t < static_cast<int>(kNumClasses)); + if (t == static_cast<int>(locked_size_class)) return false; + return central_cache[t].ShrinkCache(static_cast<int>(locked_size_class), force); +} + +bool TCMalloc_Central_FreeList::MakeCacheSpace() { + // Is there room in the cache? + if (used_slots_ < cache_size_) return true; + // Check if we can expand this cache? + if (cache_size_ == kNumTransferEntries) return false; + // Ok, we'll try to grab an entry from some other size class. + if (EvictRandomSizeClass(size_class_, false) || + EvictRandomSizeClass(size_class_, true)) { + // Succeeded in evicting, we're going to make our cache larger. + cache_size_++; + return true; + } + return false; +} + + +namespace { +class LockInverter { + private: + SpinLock *held_, *temp_; + public: + inline explicit LockInverter(SpinLock* held, SpinLock *temp) + : held_(held), temp_(temp) { held_->Unlock(); temp_->Lock(); } + inline ~LockInverter() { temp_->Unlock(); held_->Lock(); } +}; +} + +bool TCMalloc_Central_FreeList::ShrinkCache(int locked_size_class, bool force) { + // Start with a quick check without taking a lock. + if (cache_size_ == 0) return false; + // We don't evict from a full cache unless we are 'forcing'. + if (force == false && used_slots_ == cache_size_) return false; + + // Grab lock, but first release the other lock held by this thread. We use + // the lock inverter to ensure that we never hold two size class locks + // concurrently. That can create a deadlock because there is no well + // defined nesting order. + LockInverter li(¢ral_cache[locked_size_class].lock_, &lock_); + ASSERT(used_slots_ <= cache_size_); + ASSERT(0 <= cache_size_); + if (cache_size_ == 0) return false; + if (used_slots_ == cache_size_) { + if (force == false) return false; + // ReleaseListToSpans releases the lock, so we have to make all the + // updates to the central list before calling it. + cache_size_--; + used_slots_--; + ReleaseListToSpans(tc_slots_[used_slots_].head); + return true; + } + cache_size_--; + return true; +} + +void TCMalloc_Central_FreeList::InsertRange(void *start, void *end, int N) { + SpinLockHolder h(&lock_); + if (N == num_objects_to_move[size_class_] && + MakeCacheSpace()) { + int slot = used_slots_++; + ASSERT(slot >=0); + ASSERT(slot < kNumTransferEntries); + TCEntry *entry = &tc_slots_[slot]; + entry->head = start; + entry->tail = end; + return; + } + ReleaseListToSpans(start); +} + +void TCMalloc_Central_FreeList::RemoveRange(void **start, void **end, int *N) { + int num = *N; + ASSERT(num > 0); + + SpinLockHolder h(&lock_); + if (num == num_objects_to_move[size_class_] && used_slots_ > 0) { + int slot = --used_slots_; + ASSERT(slot >= 0); + TCEntry *entry = &tc_slots_[slot]; + *start = entry->head; + *end = entry->tail; + return; + } + + // TODO: Prefetch multiple TCEntries? + void *tail = FetchFromSpansSafe(); + if (!tail) { + // We are completely out of memory. + *start = *end = NULL; + *N = 0; + return; + } + + SLL_SetNext(tail, NULL); + void *head = tail; + int count = 1; + while (count < num) { + void *t = FetchFromSpans(); + if (!t) break; + SLL_Push(&head, t); + count++; + } + *start = head; + *end = tail; + *N = count; +} + + +void* TCMalloc_Central_FreeList::FetchFromSpansSafe() { + void *t = FetchFromSpans(); + if (!t) { + Populate(); + t = FetchFromSpans(); + } + return t; +} + +void* TCMalloc_Central_FreeList::FetchFromSpans() { + if (DLL_IsEmpty(&nonempty_)) return NULL; + Span* span = nonempty_.next; + + ASSERT(span->objects != NULL); + ASSERT_SPAN_COMMITTED(span); + span->refcount++; + void* result = span->objects; + span->objects = *(reinterpret_cast<void**>(result)); + if (span->objects == NULL) { + // Move to empty list + DLL_Remove(span); + DLL_Prepend(&empty_, span); + Event(span, 'E', 0); + } + counter_--; + return result; +} + +// Fetch memory from the system and add to the central cache freelist. +ALWAYS_INLINE void TCMalloc_Central_FreeList::Populate() { + // Release central list lock while operating on pageheap + lock_.Unlock(); + const size_t npages = class_to_pages[size_class_]; + + Span* span; + { + SpinLockHolder h(&pageheap_lock); + span = pageheap->New(npages); + if (span) pageheap->RegisterSizeClass(span, size_class_); + } + if (span == NULL) { +#if HAVE(ERRNO_H) + MESSAGE("allocation failed: %d\n", errno); +#elif OS(WINDOWS) + MESSAGE("allocation failed: %d\n", ::GetLastError()); +#else + MESSAGE("allocation failed\n"); +#endif + lock_.Lock(); + return; + } + ASSERT_SPAN_COMMITTED(span); + ASSERT(span->length == npages); + // Cache sizeclass info eagerly. Locking is not necessary. + // (Instead of being eager, we could just replace any stale info + // about this span, but that seems to be no better in practice.) + for (size_t i = 0; i < npages; i++) { + pageheap->CacheSizeClass(span->start + i, size_class_); + } + + // Split the block into pieces and add to the free-list + // TODO: coloring of objects to avoid cache conflicts? + void** tail = &span->objects; + char* ptr = reinterpret_cast<char*>(span->start << kPageShift); + char* limit = ptr + (npages << kPageShift); + const size_t size = ByteSizeForClass(size_class_); + int num = 0; + char* nptr; + while ((nptr = ptr + size) <= limit) { + *tail = ptr; + tail = reinterpret_cast_ptr<void**>(ptr); + ptr = nptr; + num++; + } + ASSERT(ptr <= limit); + *tail = NULL; + span->refcount = 0; // No sub-object in use yet + + // Add span to list of non-empty spans + lock_.Lock(); + DLL_Prepend(&nonempty_, span); + counter_ += num; +} + +//------------------------------------------------------------------- +// TCMalloc_ThreadCache implementation +//------------------------------------------------------------------- + +inline bool TCMalloc_ThreadCache::SampleAllocation(size_t k) { + if (bytes_until_sample_ < k) { + PickNextSample(k); + return true; + } else { + bytes_until_sample_ -= k; + return false; + } +} + +void TCMalloc_ThreadCache::Init(ThreadIdentifier tid) { + size_ = 0; + next_ = NULL; + prev_ = NULL; + tid_ = tid; + in_setspecific_ = false; + for (size_t cl = 0; cl < kNumClasses; ++cl) { + list_[cl].Init(); + } + + // Initialize RNG -- run it for a bit to get to good values + bytes_until_sample_ = 0; + rnd_ = static_cast<uint32_t>(reinterpret_cast<uintptr_t>(this)); + for (int i = 0; i < 100; i++) { + PickNextSample(static_cast<size_t>(FLAGS_tcmalloc_sample_parameter * 2)); + } +} + +void TCMalloc_ThreadCache::Cleanup() { + // Put unused memory back into central cache + for (size_t cl = 0; cl < kNumClasses; ++cl) { + if (list_[cl].length() > 0) { + ReleaseToCentralCache(cl, list_[cl].length()); + } + } +} + +ALWAYS_INLINE void* TCMalloc_ThreadCache::Allocate(size_t size) { + ASSERT(size <= kMaxSize); + const size_t cl = SizeClass(size); + FreeList* list = &list_[cl]; + size_t allocationSize = ByteSizeForClass(cl); + if (list->empty()) { + FetchFromCentralCache(cl, allocationSize); + if (list->empty()) return NULL; + } + size_ -= allocationSize; + return list->Pop(); +} + +inline void TCMalloc_ThreadCache::Deallocate(void* ptr, size_t cl) { + size_ += ByteSizeForClass(cl); + FreeList* list = &list_[cl]; + list->Push(ptr); + // If enough data is free, put back into central cache + if (list->length() > kMaxFreeListLength) { + ReleaseToCentralCache(cl, num_objects_to_move[cl]); + } + if (size_ >= per_thread_cache_size) Scavenge(); +} + +// Remove some objects of class "cl" from central cache and add to thread heap +ALWAYS_INLINE void TCMalloc_ThreadCache::FetchFromCentralCache(size_t cl, size_t allocationSize) { + int fetch_count = num_objects_to_move[cl]; + void *start, *end; + central_cache[cl].RemoveRange(&start, &end, &fetch_count); + list_[cl].PushRange(fetch_count, start, end); + size_ += allocationSize * fetch_count; +} + +// Remove some objects of class "cl" from thread heap and add to central cache +inline void TCMalloc_ThreadCache::ReleaseToCentralCache(size_t cl, int N) { + ASSERT(N > 0); + FreeList* src = &list_[cl]; + if (N > src->length()) N = src->length(); + size_ -= N*ByteSizeForClass(cl); + + // We return prepackaged chains of the correct size to the central cache. + // TODO: Use the same format internally in the thread caches? + int batch_size = num_objects_to_move[cl]; + while (N > batch_size) { + void *tail, *head; + src->PopRange(batch_size, &head, &tail); + central_cache[cl].InsertRange(head, tail, batch_size); + N -= batch_size; + } + void *tail, *head; + src->PopRange(N, &head, &tail); + central_cache[cl].InsertRange(head, tail, N); +} + +// Release idle memory to the central cache +inline void TCMalloc_ThreadCache::Scavenge() { + // If the low-water mark for the free list is L, it means we would + // not have had to allocate anything from the central cache even if + // we had reduced the free list size by L. We aim to get closer to + // that situation by dropping L/2 nodes from the free list. This + // may not release much memory, but if so we will call scavenge again + // pretty soon and the low-water marks will be high on that call. + //int64 start = CycleClock::Now(); + + for (size_t cl = 0; cl < kNumClasses; cl++) { + FreeList* list = &list_[cl]; + const int lowmark = list->lowwatermark(); + if (lowmark > 0) { + const int drop = (lowmark > 1) ? lowmark/2 : 1; + ReleaseToCentralCache(cl, drop); + } + list->clear_lowwatermark(); + } + + //int64 finish = CycleClock::Now(); + //CycleTimer ct; + //MESSAGE("GC: %.0f ns\n", ct.CyclesToUsec(finish-start)*1000.0); +} + +void TCMalloc_ThreadCache::PickNextSample(size_t k) { + // Make next "random" number + // x^32+x^22+x^2+x^1+1 is a primitive polynomial for random numbers + static const uint32_t kPoly = (1 << 22) | (1 << 2) | (1 << 1) | (1 << 0); + uint32_t r = rnd_; + rnd_ = (r << 1) ^ ((static_cast<int32_t>(r) >> 31) & kPoly); + + // Next point is "rnd_ % (sample_period)". I.e., average + // increment is "sample_period/2". + const int flag_value = static_cast<int>(FLAGS_tcmalloc_sample_parameter); + static int last_flag_value = -1; + + if (flag_value != last_flag_value) { + SpinLockHolder h(&sample_period_lock); + int i; + for (i = 0; i < (static_cast<int>(sizeof(primes_list)/sizeof(primes_list[0])) - 1); i++) { + if (primes_list[i] >= flag_value) { + break; + } + } + sample_period = primes_list[i]; + last_flag_value = flag_value; + } + + bytes_until_sample_ += rnd_ % sample_period; + + if (k > (static_cast<size_t>(-1) >> 2)) { + // If the user has asked for a huge allocation then it is possible + // for the code below to loop infinitely. Just return (note that + // this throws off the sampling accuracy somewhat, but a user who + // is allocating more than 1G of memory at a time can live with a + // minor inaccuracy in profiling of small allocations, and also + // would rather not wait for the loop below to terminate). + return; + } + + while (bytes_until_sample_ < k) { + // Increase bytes_until_sample_ by enough average sampling periods + // (sample_period >> 1) to allow us to sample past the current + // allocation. + bytes_until_sample_ += (sample_period >> 1); + } + + bytes_until_sample_ -= k; +} + +void TCMalloc_ThreadCache::InitModule() { + // There is a slight potential race here because of double-checked + // locking idiom. However, as long as the program does a small + // allocation before switching to multi-threaded mode, we will be + // fine. We increase the chances of doing such a small allocation + // by doing one in the constructor of the module_enter_exit_hook + // object declared below. + SpinLockHolder h(&pageheap_lock); + if (!phinited) { +#ifdef WTF_CHANGES + InitTSD(); +#endif + InitSizeClasses(); + threadheap_allocator.Init(); + span_allocator.Init(); + span_allocator.New(); // Reduce cache conflicts + span_allocator.New(); // Reduce cache conflicts + stacktrace_allocator.Init(); + DLL_Init(&sampled_objects); + for (size_t i = 0; i < kNumClasses; ++i) { + central_cache[i].Init(i); + } + pageheap->init(); + phinited = 1; +#if defined(WTF_CHANGES) && OS(DARWIN) + FastMallocZone::init(); +#endif + } +} + +inline TCMalloc_ThreadCache* TCMalloc_ThreadCache::NewHeap(ThreadIdentifier tid) { + // Create the heap and add it to the linked list + TCMalloc_ThreadCache *heap = threadheap_allocator.New(); + heap->Init(tid); + heap->next_ = thread_heaps; + heap->prev_ = NULL; + if (thread_heaps != NULL) thread_heaps->prev_ = heap; + thread_heaps = heap; + thread_heap_count++; + RecomputeThreadCacheSize(); + return heap; +} + +inline TCMalloc_ThreadCache* TCMalloc_ThreadCache::GetThreadHeap() { +#ifdef HAVE_TLS + // __thread is faster, but only when the kernel supports it + if (KernelSupportsTLS()) + return threadlocal_heap; +#elif OS(WINDOWS) + return static_cast<TCMalloc_ThreadCache*>(TlsGetValue(tlsIndex)); +#else + return static_cast<TCMalloc_ThreadCache*>(pthread_getspecific(heap_key)); +#endif +} + +inline TCMalloc_ThreadCache* TCMalloc_ThreadCache::GetCache() { + TCMalloc_ThreadCache* ptr = NULL; + if (!tsd_inited) { + InitModule(); + } else { + ptr = GetThreadHeap(); + } + if (ptr == NULL) ptr = CreateCacheIfNecessary(); + return ptr; +} + +// In deletion paths, we do not try to create a thread-cache. This is +// because we may be in the thread destruction code and may have +// already cleaned up the cache for this thread. +inline TCMalloc_ThreadCache* TCMalloc_ThreadCache::GetCacheIfPresent() { + if (!tsd_inited) return NULL; + void* const p = GetThreadHeap(); + return reinterpret_cast<TCMalloc_ThreadCache*>(p); +} + +void TCMalloc_ThreadCache::InitTSD() { + ASSERT(!tsd_inited); +#if USE(PTHREAD_GETSPECIFIC_DIRECT) + pthread_key_init_np(heap_key, DestroyThreadCache); +#else + pthread_key_create(&heap_key, DestroyThreadCache); +#endif +#if OS(WINDOWS) + tlsIndex = TlsAlloc(); +#endif + tsd_inited = true; + +#if !OS(WINDOWS) + // We may have used a fake pthread_t for the main thread. Fix it. + pthread_t zero; + memset(&zero, 0, sizeof(zero)); +#endif +#ifndef WTF_CHANGES + SpinLockHolder h(&pageheap_lock); +#else + ASSERT(pageheap_lock.IsHeld()); +#endif + for (TCMalloc_ThreadCache* h = thread_heaps; h != NULL; h = h->next_) { +#if OS(WINDOWS) + if (h->tid_ == 0) { + h->tid_ = GetCurrentThreadId(); + } +#else + if (pthread_equal(h->tid_, zero)) { + h->tid_ = pthread_self(); + } +#endif + } +} + +TCMalloc_ThreadCache* TCMalloc_ThreadCache::CreateCacheIfNecessary() { + // Initialize per-thread data if necessary + TCMalloc_ThreadCache* heap = NULL; + { + SpinLockHolder h(&pageheap_lock); + +#if OS(WINDOWS) + DWORD me; + if (!tsd_inited) { + me = 0; + } else { + me = GetCurrentThreadId(); + } +#else + // Early on in glibc's life, we cannot even call pthread_self() + pthread_t me; + if (!tsd_inited) { + memset(&me, 0, sizeof(me)); + } else { + me = pthread_self(); + } +#endif + + // This may be a recursive malloc call from pthread_setspecific() + // In that case, the heap for this thread has already been created + // and added to the linked list. So we search for that first. + for (TCMalloc_ThreadCache* h = thread_heaps; h != NULL; h = h->next_) { +#if OS(WINDOWS) + if (h->tid_ == me) { +#else + if (pthread_equal(h->tid_, me)) { +#endif + heap = h; + break; + } + } + + if (heap == NULL) heap = NewHeap(me); + } + + // We call pthread_setspecific() outside the lock because it may + // call malloc() recursively. The recursive call will never get + // here again because it will find the already allocated heap in the + // linked list of heaps. + if (!heap->in_setspecific_ && tsd_inited) { + heap->in_setspecific_ = true; + setThreadHeap(heap); + } + return heap; +} + +void TCMalloc_ThreadCache::BecomeIdle() { + if (!tsd_inited) return; // No caches yet + TCMalloc_ThreadCache* heap = GetThreadHeap(); + if (heap == NULL) return; // No thread cache to remove + if (heap->in_setspecific_) return; // Do not disturb the active caller + + heap->in_setspecific_ = true; + setThreadHeap(NULL); +#ifdef HAVE_TLS + // Also update the copy in __thread + threadlocal_heap = NULL; +#endif + heap->in_setspecific_ = false; + if (GetThreadHeap() == heap) { + // Somehow heap got reinstated by a recursive call to malloc + // from pthread_setspecific. We give up in this case. + return; + } + + // We can now get rid of the heap + DeleteCache(heap); +} + +void TCMalloc_ThreadCache::DestroyThreadCache(void* ptr) { + // Note that "ptr" cannot be NULL since pthread promises not + // to invoke the destructor on NULL values, but for safety, + // we check anyway. + if (ptr == NULL) return; +#ifdef HAVE_TLS + // Prevent fast path of GetThreadHeap() from returning heap. + threadlocal_heap = NULL; +#endif + DeleteCache(reinterpret_cast<TCMalloc_ThreadCache*>(ptr)); +} + +void TCMalloc_ThreadCache::DeleteCache(TCMalloc_ThreadCache* heap) { + // Remove all memory from heap + heap->Cleanup(); + + // Remove from linked list + SpinLockHolder h(&pageheap_lock); + if (heap->next_ != NULL) heap->next_->prev_ = heap->prev_; + if (heap->prev_ != NULL) heap->prev_->next_ = heap->next_; + if (thread_heaps == heap) thread_heaps = heap->next_; + thread_heap_count--; + RecomputeThreadCacheSize(); + + threadheap_allocator.Delete(heap); +} + +void TCMalloc_ThreadCache::RecomputeThreadCacheSize() { + // Divide available space across threads + int n = thread_heap_count > 0 ? thread_heap_count : 1; + size_t space = overall_thread_cache_size / n; + + // Limit to allowed range + if (space < kMinThreadCacheSize) space = kMinThreadCacheSize; + if (space > kMaxThreadCacheSize) space = kMaxThreadCacheSize; + + per_thread_cache_size = space; +} + +void TCMalloc_ThreadCache::Print() const { + for (size_t cl = 0; cl < kNumClasses; ++cl) { + MESSAGE(" %5" PRIuS " : %4d len; %4d lo\n", + ByteSizeForClass(cl), + list_[cl].length(), + list_[cl].lowwatermark()); + } +} + +// Extract interesting stats +struct TCMallocStats { + uint64_t system_bytes; // Bytes alloced from system + uint64_t thread_bytes; // Bytes in thread caches + uint64_t central_bytes; // Bytes in central cache + uint64_t transfer_bytes; // Bytes in central transfer cache + uint64_t pageheap_bytes; // Bytes in page heap + uint64_t metadata_bytes; // Bytes alloced for metadata +}; + +#ifndef WTF_CHANGES +// Get stats into "r". Also get per-size-class counts if class_count != NULL +static void ExtractStats(TCMallocStats* r, uint64_t* class_count) { + r->central_bytes = 0; + r->transfer_bytes = 0; + for (int cl = 0; cl < kNumClasses; ++cl) { + const int length = central_cache[cl].length(); + const int tc_length = central_cache[cl].tc_length(); + r->central_bytes += static_cast<uint64_t>(ByteSizeForClass(cl)) * length; + r->transfer_bytes += + static_cast<uint64_t>(ByteSizeForClass(cl)) * tc_length; + if (class_count) class_count[cl] = length + tc_length; + } + + // Add stats from per-thread heaps + r->thread_bytes = 0; + { // scope + SpinLockHolder h(&pageheap_lock); + for (TCMalloc_ThreadCache* h = thread_heaps; h != NULL; h = h->next_) { + r->thread_bytes += h->Size(); + if (class_count) { + for (size_t cl = 0; cl < kNumClasses; ++cl) { + class_count[cl] += h->freelist_length(cl); + } + } + } + } + + { //scope + SpinLockHolder h(&pageheap_lock); + r->system_bytes = pageheap->SystemBytes(); + r->metadata_bytes = metadata_system_bytes; + r->pageheap_bytes = pageheap->FreeBytes(); + } +} +#endif + +#ifndef WTF_CHANGES +// WRITE stats to "out" +static void DumpStats(TCMalloc_Printer* out, int level) { + TCMallocStats stats; + uint64_t class_count[kNumClasses]; + ExtractStats(&stats, (level >= 2 ? class_count : NULL)); + + if (level >= 2) { + out->printf("------------------------------------------------\n"); + uint64_t cumulative = 0; + for (int cl = 0; cl < kNumClasses; ++cl) { + if (class_count[cl] > 0) { + uint64_t class_bytes = class_count[cl] * ByteSizeForClass(cl); + cumulative += class_bytes; + out->printf("class %3d [ %8" PRIuS " bytes ] : " + "%8" PRIu64 " objs; %5.1f MB; %5.1f cum MB\n", + cl, ByteSizeForClass(cl), + class_count[cl], + class_bytes / 1048576.0, + cumulative / 1048576.0); + } + } + + SpinLockHolder h(&pageheap_lock); + pageheap->Dump(out); + } + + const uint64_t bytes_in_use = stats.system_bytes + - stats.pageheap_bytes + - stats.central_bytes + - stats.transfer_bytes + - stats.thread_bytes; + + out->printf("------------------------------------------------\n" + "MALLOC: %12" PRIu64 " Heap size\n" + "MALLOC: %12" PRIu64 " Bytes in use by application\n" + "MALLOC: %12" PRIu64 " Bytes free in page heap\n" + "MALLOC: %12" PRIu64 " Bytes free in central cache\n" + "MALLOC: %12" PRIu64 " Bytes free in transfer cache\n" + "MALLOC: %12" PRIu64 " Bytes free in thread caches\n" + "MALLOC: %12" PRIu64 " Spans in use\n" + "MALLOC: %12" PRIu64 " Thread heaps in use\n" + "MALLOC: %12" PRIu64 " Metadata allocated\n" + "------------------------------------------------\n", + stats.system_bytes, + bytes_in_use, + stats.pageheap_bytes, + stats.central_bytes, + stats.transfer_bytes, + stats.thread_bytes, + uint64_t(span_allocator.inuse()), + uint64_t(threadheap_allocator.inuse()), + stats.metadata_bytes); +} + +static void PrintStats(int level) { + const int kBufferSize = 16 << 10; + char* buffer = new char[kBufferSize]; + TCMalloc_Printer printer(buffer, kBufferSize); + DumpStats(&printer, level); + write(STDERR_FILENO, buffer, strlen(buffer)); + delete[] buffer; +} + +static void** DumpStackTraces() { + // Count how much space we need + int needed_slots = 0; + { + SpinLockHolder h(&pageheap_lock); + for (Span* s = sampled_objects.next; s != &sampled_objects; s = s->next) { + StackTrace* stack = reinterpret_cast<StackTrace*>(s->objects); + needed_slots += 3 + stack->depth; + } + needed_slots += 100; // Slop in case sample grows + needed_slots += needed_slots/8; // An extra 12.5% slop + } + + void** result = new void*[needed_slots]; + if (result == NULL) { + MESSAGE("tcmalloc: could not allocate %d slots for stack traces\n", + needed_slots); + return NULL; + } + + SpinLockHolder h(&pageheap_lock); + int used_slots = 0; + for (Span* s = sampled_objects.next; s != &sampled_objects; s = s->next) { + ASSERT(used_slots < needed_slots); // Need to leave room for terminator + StackTrace* stack = reinterpret_cast<StackTrace*>(s->objects); + if (used_slots + 3 + stack->depth >= needed_slots) { + // No more room + break; + } + + result[used_slots+0] = reinterpret_cast<void*>(static_cast<uintptr_t>(1)); + result[used_slots+1] = reinterpret_cast<void*>(stack->size); + result[used_slots+2] = reinterpret_cast<void*>(stack->depth); + for (int d = 0; d < stack->depth; d++) { + result[used_slots+3+d] = stack->stack[d]; + } + used_slots += 3 + stack->depth; + } + result[used_slots] = reinterpret_cast<void*>(static_cast<uintptr_t>(0)); + return result; +} +#endif + +#ifndef WTF_CHANGES + +// TCMalloc's support for extra malloc interfaces +class TCMallocImplementation : public MallocExtension { + public: + virtual void GetStats(char* buffer, int buffer_length) { + ASSERT(buffer_length > 0); + TCMalloc_Printer printer(buffer, buffer_length); + + // Print level one stats unless lots of space is available + if (buffer_length < 10000) { + DumpStats(&printer, 1); + } else { + DumpStats(&printer, 2); + } + } + + virtual void** ReadStackTraces() { + return DumpStackTraces(); + } + + virtual bool GetNumericProperty(const char* name, size_t* value) { + ASSERT(name != NULL); + + if (strcmp(name, "generic.current_allocated_bytes") == 0) { + TCMallocStats stats; + ExtractStats(&stats, NULL); + *value = stats.system_bytes + - stats.thread_bytes + - stats.central_bytes + - stats.pageheap_bytes; + return true; + } + + if (strcmp(name, "generic.heap_size") == 0) { + TCMallocStats stats; + ExtractStats(&stats, NULL); + *value = stats.system_bytes; + return true; + } + + if (strcmp(name, "tcmalloc.slack_bytes") == 0) { + // We assume that bytes in the page heap are not fragmented too + // badly, and are therefore available for allocation. + SpinLockHolder l(&pageheap_lock); + *value = pageheap->FreeBytes(); + return true; + } + + if (strcmp(name, "tcmalloc.max_total_thread_cache_bytes") == 0) { + SpinLockHolder l(&pageheap_lock); + *value = overall_thread_cache_size; + return true; + } + + if (strcmp(name, "tcmalloc.current_total_thread_cache_bytes") == 0) { + TCMallocStats stats; + ExtractStats(&stats, NULL); + *value = stats.thread_bytes; + return true; + } + + return false; + } + + virtual bool SetNumericProperty(const char* name, size_t value) { + ASSERT(name != NULL); + + if (strcmp(name, "tcmalloc.max_total_thread_cache_bytes") == 0) { + // Clip the value to a reasonable range + if (value < kMinThreadCacheSize) value = kMinThreadCacheSize; + if (value > (1<<30)) value = (1<<30); // Limit to 1GB + + SpinLockHolder l(&pageheap_lock); + overall_thread_cache_size = static_cast<size_t>(value); + TCMalloc_ThreadCache::RecomputeThreadCacheSize(); + return true; + } + + return false; + } + + virtual void MarkThreadIdle() { + TCMalloc_ThreadCache::BecomeIdle(); + } + + virtual void ReleaseFreeMemory() { + SpinLockHolder h(&pageheap_lock); + pageheap->ReleaseFreePages(); + } +}; +#endif + +// The constructor allocates an object to ensure that initialization +// runs before main(), and therefore we do not have a chance to become +// multi-threaded before initialization. We also create the TSD key +// here. Presumably by the time this constructor runs, glibc is in +// good enough shape to handle pthread_key_create(). +// +// The constructor also takes the opportunity to tell STL to use +// tcmalloc. We want to do this early, before construct time, so +// all user STL allocations go through tcmalloc (which works really +// well for STL). +// +// The destructor prints stats when the program exits. +class TCMallocGuard { + public: + + TCMallocGuard() { +#ifdef HAVE_TLS // this is true if the cc/ld/libc combo support TLS + // Check whether the kernel also supports TLS (needs to happen at runtime) + CheckIfKernelSupportsTLS(); +#endif +#ifndef WTF_CHANGES +#ifdef WIN32 // patch the windows VirtualAlloc, etc. + PatchWindowsFunctions(); // defined in windows/patch_functions.cc +#endif +#endif + free(malloc(1)); + TCMalloc_ThreadCache::InitTSD(); + free(malloc(1)); +#ifndef WTF_CHANGES + MallocExtension::Register(new TCMallocImplementation); +#endif + } + +#ifndef WTF_CHANGES + ~TCMallocGuard() { + const char* env = getenv("MALLOCSTATS"); + if (env != NULL) { + int level = atoi(env); + if (level < 1) level = 1; + PrintStats(level); + } +#ifdef WIN32 + UnpatchWindowsFunctions(); +#endif + } +#endif +}; + +#ifndef WTF_CHANGES +static TCMallocGuard module_enter_exit_hook; +#endif + + +//------------------------------------------------------------------- +// Helpers for the exported routines below +//------------------------------------------------------------------- + +#ifndef WTF_CHANGES + +static Span* DoSampledAllocation(size_t size) { + + // Grab the stack trace outside the heap lock + StackTrace tmp; + tmp.depth = GetStackTrace(tmp.stack, kMaxStackDepth, 1); + tmp.size = size; + + SpinLockHolder h(&pageheap_lock); + // Allocate span + Span *span = pageheap->New(pages(size == 0 ? 1 : size)); + if (span == NULL) { + return NULL; + } + + // Allocate stack trace + StackTrace *stack = stacktrace_allocator.New(); + if (stack == NULL) { + // Sampling failed because of lack of memory + return span; + } + + *stack = tmp; + span->sample = 1; + span->objects = stack; + DLL_Prepend(&sampled_objects, span); + + return span; +} +#endif + +static inline bool CheckCachedSizeClass(void *ptr) { + PageID p = reinterpret_cast<uintptr_t>(ptr) >> kPageShift; + size_t cached_value = pageheap->GetSizeClassIfCached(p); + return cached_value == 0 || + cached_value == pageheap->GetDescriptor(p)->sizeclass; +} + +static inline void* CheckedMallocResult(void *result) +{ + ASSERT(result == 0 || CheckCachedSizeClass(result)); + return result; +} + +static inline void* SpanToMallocResult(Span *span) { + ASSERT_SPAN_COMMITTED(span); + pageheap->CacheSizeClass(span->start, 0); + return + CheckedMallocResult(reinterpret_cast<void*>(span->start << kPageShift)); +} + +#ifdef WTF_CHANGES +template <bool crashOnFailure> +#endif +static ALWAYS_INLINE void* do_malloc(size_t size) { + void* ret = NULL; + +#ifdef WTF_CHANGES + ASSERT(!isForbidden()); +#endif + + // The following call forces module initialization + TCMalloc_ThreadCache* heap = TCMalloc_ThreadCache::GetCache(); +#ifndef WTF_CHANGES + if ((FLAGS_tcmalloc_sample_parameter > 0) && heap->SampleAllocation(size)) { + Span* span = DoSampledAllocation(size); + if (span != NULL) { + ret = SpanToMallocResult(span); + } + } else +#endif + if (size > kMaxSize) { + // Use page-level allocator + SpinLockHolder h(&pageheap_lock); + Span* span = pageheap->New(pages(size)); + if (span != NULL) { + ret = SpanToMallocResult(span); + } + } else { + // The common case, and also the simplest. This just pops the + // size-appropriate freelist, afer replenishing it if it's empty. + ret = CheckedMallocResult(heap->Allocate(size)); + } + if (!ret) { +#ifdef WTF_CHANGES + if (crashOnFailure) // This branch should be optimized out by the compiler. + CRASH(); +#else + errno = ENOMEM; +#endif + } + return ret; +} + +static ALWAYS_INLINE void do_free(void* ptr) { + if (ptr == NULL) return; + ASSERT(pageheap != NULL); // Should not call free() before malloc() + const PageID p = reinterpret_cast<uintptr_t>(ptr) >> kPageShift; + Span* span = NULL; + size_t cl = pageheap->GetSizeClassIfCached(p); + + if (cl == 0) { + span = pageheap->GetDescriptor(p); + cl = span->sizeclass; + pageheap->CacheSizeClass(p, cl); + } + if (cl != 0) { +#ifndef NO_TCMALLOC_SAMPLES + ASSERT(!pageheap->GetDescriptor(p)->sample); +#endif + TCMalloc_ThreadCache* heap = TCMalloc_ThreadCache::GetCacheIfPresent(); + if (heap != NULL) { + heap->Deallocate(ptr, cl); + } else { + // Delete directly into central cache + SLL_SetNext(ptr, NULL); + central_cache[cl].InsertRange(ptr, ptr, 1); + } + } else { + SpinLockHolder h(&pageheap_lock); + ASSERT(reinterpret_cast<uintptr_t>(ptr) % kPageSize == 0); + ASSERT(span != NULL && span->start == p); +#ifndef NO_TCMALLOC_SAMPLES + if (span->sample) { + DLL_Remove(span); + stacktrace_allocator.Delete(reinterpret_cast<StackTrace*>(span->objects)); + span->objects = NULL; + } +#endif + pageheap->Delete(span); + } +} + +#ifndef WTF_CHANGES +// For use by exported routines below that want specific alignments +// +// Note: this code can be slow, and can significantly fragment memory. +// The expectation is that memalign/posix_memalign/valloc/pvalloc will +// not be invoked very often. This requirement simplifies our +// implementation and allows us to tune for expected allocation +// patterns. +static void* do_memalign(size_t align, size_t size) { + ASSERT((align & (align - 1)) == 0); + ASSERT(align > 0); + if (pageheap == NULL) TCMalloc_ThreadCache::InitModule(); + + // Allocate at least one byte to avoid boundary conditions below + if (size == 0) size = 1; + + if (size <= kMaxSize && align < kPageSize) { + // Search through acceptable size classes looking for one with + // enough alignment. This depends on the fact that + // InitSizeClasses() currently produces several size classes that + // are aligned at powers of two. We will waste time and space if + // we miss in the size class array, but that is deemed acceptable + // since memalign() should be used rarely. + size_t cl = SizeClass(size); + while (cl < kNumClasses && ((class_to_size[cl] & (align - 1)) != 0)) { + cl++; + } + if (cl < kNumClasses) { + TCMalloc_ThreadCache* heap = TCMalloc_ThreadCache::GetCache(); + return CheckedMallocResult(heap->Allocate(class_to_size[cl])); + } + } + + // We will allocate directly from the page heap + SpinLockHolder h(&pageheap_lock); + + if (align <= kPageSize) { + // Any page-level allocation will be fine + // TODO: We could put the rest of this page in the appropriate + // TODO: cache but it does not seem worth it. + Span* span = pageheap->New(pages(size)); + return span == NULL ? NULL : SpanToMallocResult(span); + } + + // Allocate extra pages and carve off an aligned portion + const Length alloc = pages(size + align); + Span* span = pageheap->New(alloc); + if (span == NULL) return NULL; + + // Skip starting portion so that we end up aligned + Length skip = 0; + while ((((span->start+skip) << kPageShift) & (align - 1)) != 0) { + skip++; + } + ASSERT(skip < alloc); + if (skip > 0) { + Span* rest = pageheap->Split(span, skip); + pageheap->Delete(span); + span = rest; + } + + // Skip trailing portion that we do not need to return + const Length needed = pages(size); + ASSERT(span->length >= needed); + if (span->length > needed) { + Span* trailer = pageheap->Split(span, needed); + pageheap->Delete(trailer); + } + return SpanToMallocResult(span); +} +#endif + +// Helpers for use by exported routines below: + +#ifndef WTF_CHANGES +static inline void do_malloc_stats() { + PrintStats(1); +} +#endif + +static inline int do_mallopt(int, int) { + return 1; // Indicates error +} + +#ifdef HAVE_STRUCT_MALLINFO // mallinfo isn't defined on freebsd, for instance +static inline struct mallinfo do_mallinfo() { + TCMallocStats stats; + ExtractStats(&stats, NULL); + + // Just some of the fields are filled in. + struct mallinfo info; + memset(&info, 0, sizeof(info)); + + // Unfortunately, the struct contains "int" field, so some of the + // size values will be truncated. + info.arena = static_cast<int>(stats.system_bytes); + info.fsmblks = static_cast<int>(stats.thread_bytes + + stats.central_bytes + + stats.transfer_bytes); + info.fordblks = static_cast<int>(stats.pageheap_bytes); + info.uordblks = static_cast<int>(stats.system_bytes + - stats.thread_bytes + - stats.central_bytes + - stats.transfer_bytes + - stats.pageheap_bytes); + + return info; +} +#endif + +//------------------------------------------------------------------- +// Exported routines +//------------------------------------------------------------------- + +// CAVEAT: The code structure below ensures that MallocHook methods are always +// called from the stack frame of the invoked allocation function. +// heap-checker.cc depends on this to start a stack trace from +// the call to the (de)allocation function. + +#ifndef WTF_CHANGES +extern "C" +#else +#define do_malloc do_malloc<crashOnFailure> + +template <bool crashOnFailure> +ALWAYS_INLINE void* malloc(size_t); + +void* fastMalloc(size_t size) +{ + return malloc<true>(size); +} + +TryMallocReturnValue tryFastMalloc(size_t size) +{ + return malloc<false>(size); +} + +template <bool crashOnFailure> +ALWAYS_INLINE +#endif +void* malloc(size_t size) { +#if ENABLE(WTF_MALLOC_VALIDATION) + if (std::numeric_limits<size_t>::max() - Internal::ValidationBufferSize <= size) // If overflow would occur... + return 0; + void* result = do_malloc(size + Internal::ValidationBufferSize); + if (!result) + return 0; + + Internal::ValidationHeader* header = static_cast<Internal::ValidationHeader*>(result); + header->m_size = size; + header->m_type = Internal::AllocTypeMalloc; + header->m_prefix = static_cast<unsigned>(Internal::ValidationPrefix); + result = header + 1; + *Internal::fastMallocValidationSuffix(result) = Internal::ValidationSuffix; + fastMallocValidate(result); +#else + void* result = do_malloc(size); +#endif + +#ifndef WTF_CHANGES + MallocHook::InvokeNewHook(result, size); +#endif + return result; +} + +#ifndef WTF_CHANGES +extern "C" +#endif +void free(void* ptr) { +#ifndef WTF_CHANGES + MallocHook::InvokeDeleteHook(ptr); +#endif + +#if ENABLE(WTF_MALLOC_VALIDATION) + if (!ptr) + return; + + fastMallocValidate(ptr); + Internal::ValidationHeader* header = Internal::fastMallocValidationHeader(ptr); + memset(ptr, 0xCC, header->m_size); + do_free(header); +#else + do_free(ptr); +#endif +} + +#ifndef WTF_CHANGES +extern "C" +#else +template <bool crashOnFailure> +ALWAYS_INLINE void* calloc(size_t, size_t); + +void* fastCalloc(size_t n, size_t elem_size) +{ + void* result = calloc<true>(n, elem_size); +#if ENABLE(WTF_MALLOC_VALIDATION) + fastMallocValidate(result); +#endif + return result; +} + +TryMallocReturnValue tryFastCalloc(size_t n, size_t elem_size) +{ + void* result = calloc<false>(n, elem_size); +#if ENABLE(WTF_MALLOC_VALIDATION) + fastMallocValidate(result); +#endif + return result; +} + +template <bool crashOnFailure> +ALWAYS_INLINE +#endif +void* calloc(size_t n, size_t elem_size) { + size_t totalBytes = n * elem_size; + + // Protect against overflow + if (n > 1 && elem_size && (totalBytes / elem_size) != n) + return 0; + +#if ENABLE(WTF_MALLOC_VALIDATION) + void* result = malloc<crashOnFailure>(totalBytes); + if (!result) + return 0; + + memset(result, 0, totalBytes); + fastMallocValidate(result); +#else + void* result = do_malloc(totalBytes); + if (result != NULL) { + memset(result, 0, totalBytes); + } +#endif + +#ifndef WTF_CHANGES + MallocHook::InvokeNewHook(result, totalBytes); +#endif + return result; +} + +// Since cfree isn't used anywhere, we don't compile it in. +#ifndef WTF_CHANGES +#ifndef WTF_CHANGES +extern "C" +#endif +void cfree(void* ptr) { +#ifndef WTF_CHANGES + MallocHook::InvokeDeleteHook(ptr); +#endif + do_free(ptr); +} +#endif + +#ifndef WTF_CHANGES +extern "C" +#else +template <bool crashOnFailure> +ALWAYS_INLINE void* realloc(void*, size_t); + +void* fastRealloc(void* old_ptr, size_t new_size) +{ +#if ENABLE(WTF_MALLOC_VALIDATION) + fastMallocValidate(old_ptr); +#endif + void* result = realloc<true>(old_ptr, new_size); +#if ENABLE(WTF_MALLOC_VALIDATION) + fastMallocValidate(result); +#endif + return result; +} + +TryMallocReturnValue tryFastRealloc(void* old_ptr, size_t new_size) +{ +#if ENABLE(WTF_MALLOC_VALIDATION) + fastMallocValidate(old_ptr); +#endif + void* result = realloc<false>(old_ptr, new_size); +#if ENABLE(WTF_MALLOC_VALIDATION) + fastMallocValidate(result); +#endif + return result; +} + +template <bool crashOnFailure> +ALWAYS_INLINE +#endif +void* realloc(void* old_ptr, size_t new_size) { + if (old_ptr == NULL) { +#if ENABLE(WTF_MALLOC_VALIDATION) + void* result = malloc<crashOnFailure>(new_size); +#else + void* result = do_malloc(new_size); +#ifndef WTF_CHANGES + MallocHook::InvokeNewHook(result, new_size); +#endif +#endif + return result; + } + if (new_size == 0) { +#ifndef WTF_CHANGES + MallocHook::InvokeDeleteHook(old_ptr); +#endif + free(old_ptr); + return NULL; + } + +#if ENABLE(WTF_MALLOC_VALIDATION) + if (std::numeric_limits<size_t>::max() - Internal::ValidationBufferSize <= new_size) // If overflow would occur... + return 0; + Internal::ValidationHeader* header = Internal::fastMallocValidationHeader(old_ptr); + fastMallocValidate(old_ptr); + old_ptr = header; + header->m_size = new_size; + new_size += Internal::ValidationBufferSize; +#endif + + // Get the size of the old entry + const PageID p = reinterpret_cast<uintptr_t>(old_ptr) >> kPageShift; + size_t cl = pageheap->GetSizeClassIfCached(p); + Span *span = NULL; + size_t old_size; + if (cl == 0) { + span = pageheap->GetDescriptor(p); + cl = span->sizeclass; + pageheap->CacheSizeClass(p, cl); + } + if (cl != 0) { + old_size = ByteSizeForClass(cl); + } else { + ASSERT(span != NULL); + old_size = span->length << kPageShift; + } + + // Reallocate if the new size is larger than the old size, + // or if the new size is significantly smaller than the old size. + if ((new_size > old_size) || (AllocationSize(new_size) < old_size)) { + // Need to reallocate + void* new_ptr = do_malloc(new_size); + if (new_ptr == NULL) { + return NULL; + } +#ifndef WTF_CHANGES + MallocHook::InvokeNewHook(new_ptr, new_size); +#endif + memcpy(new_ptr, old_ptr, ((old_size < new_size) ? old_size : new_size)); +#ifndef WTF_CHANGES + MallocHook::InvokeDeleteHook(old_ptr); +#endif + // We could use a variant of do_free() that leverages the fact + // that we already know the sizeclass of old_ptr. The benefit + // would be small, so don't bother. + do_free(old_ptr); +#if ENABLE(WTF_MALLOC_VALIDATION) + new_ptr = static_cast<Internal::ValidationHeader*>(new_ptr) + 1; + *Internal::fastMallocValidationSuffix(new_ptr) = Internal::ValidationSuffix; +#endif + return new_ptr; + } else { +#if ENABLE(WTF_MALLOC_VALIDATION) + old_ptr = static_cast<Internal::ValidationHeader*>(old_ptr) + 1; // Set old_ptr back to the user pointer. + *Internal::fastMallocValidationSuffix(old_ptr) = Internal::ValidationSuffix; +#endif + return old_ptr; + } +} + +#ifdef WTF_CHANGES +#undef do_malloc +#else + +static SpinLock set_new_handler_lock = SPINLOCK_INITIALIZER; + +static inline void* cpp_alloc(size_t size, bool nothrow) { + for (;;) { + void* p = do_malloc(size); +#ifdef PREANSINEW + return p; +#else + if (p == NULL) { // allocation failed + // Get the current new handler. NB: this function is not + // thread-safe. We make a feeble stab at making it so here, but + // this lock only protects against tcmalloc interfering with + // itself, not with other libraries calling set_new_handler. + std::new_handler nh; + { + SpinLockHolder h(&set_new_handler_lock); + nh = std::set_new_handler(0); + (void) std::set_new_handler(nh); + } + // If no new_handler is established, the allocation failed. + if (!nh) { + if (nothrow) return 0; + throw std::bad_alloc(); + } + // Otherwise, try the new_handler. If it returns, retry the + // allocation. If it throws std::bad_alloc, fail the allocation. + // if it throws something else, don't interfere. + try { + (*nh)(); + } catch (const std::bad_alloc&) { + if (!nothrow) throw; + return p; + } + } else { // allocation success + return p; + } +#endif + } +} + +#if ENABLE(GLOBAL_FASTMALLOC_NEW) + +void* operator new(size_t size) { + void* p = cpp_alloc(size, false); + // We keep this next instruction out of cpp_alloc for a reason: when + // it's in, and new just calls cpp_alloc, the optimizer may fold the + // new call into cpp_alloc, which messes up our whole section-based + // stacktracing (see ATTRIBUTE_SECTION, above). This ensures cpp_alloc + // isn't the last thing this fn calls, and prevents the folding. + MallocHook::InvokeNewHook(p, size); + return p; +} + +void* operator new(size_t size, const std::nothrow_t&) __THROW { + void* p = cpp_alloc(size, true); + MallocHook::InvokeNewHook(p, size); + return p; +} + +void operator delete(void* p) __THROW { + MallocHook::InvokeDeleteHook(p); + do_free(p); +} + +void operator delete(void* p, const std::nothrow_t&) __THROW { + MallocHook::InvokeDeleteHook(p); + do_free(p); +} + +void* operator new[](size_t size) { + void* p = cpp_alloc(size, false); + // We keep this next instruction out of cpp_alloc for a reason: when + // it's in, and new just calls cpp_alloc, the optimizer may fold the + // new call into cpp_alloc, which messes up our whole section-based + // stacktracing (see ATTRIBUTE_SECTION, above). This ensures cpp_alloc + // isn't the last thing this fn calls, and prevents the folding. + MallocHook::InvokeNewHook(p, size); + return p; +} + +void* operator new[](size_t size, const std::nothrow_t&) __THROW { + void* p = cpp_alloc(size, true); + MallocHook::InvokeNewHook(p, size); + return p; +} + +void operator delete[](void* p) __THROW { + MallocHook::InvokeDeleteHook(p); + do_free(p); +} + +void operator delete[](void* p, const std::nothrow_t&) __THROW { + MallocHook::InvokeDeleteHook(p); + do_free(p); +} + +#endif + +extern "C" void* memalign(size_t align, size_t size) __THROW { + void* result = do_memalign(align, size); + MallocHook::InvokeNewHook(result, size); + return result; +} + +extern "C" int posix_memalign(void** result_ptr, size_t align, size_t size) + __THROW { + if (((align % sizeof(void*)) != 0) || + ((align & (align - 1)) != 0) || + (align == 0)) { + return EINVAL; + } + + void* result = do_memalign(align, size); + MallocHook::InvokeNewHook(result, size); + if (result == NULL) { + return ENOMEM; + } else { + *result_ptr = result; + return 0; + } +} + +static size_t pagesize = 0; + +extern "C" void* valloc(size_t size) __THROW { + // Allocate page-aligned object of length >= size bytes + if (pagesize == 0) pagesize = getpagesize(); + void* result = do_memalign(pagesize, size); + MallocHook::InvokeNewHook(result, size); + return result; +} + +extern "C" void* pvalloc(size_t size) __THROW { + // Round up size to a multiple of pagesize + if (pagesize == 0) pagesize = getpagesize(); + size = (size + pagesize - 1) & ~(pagesize - 1); + void* result = do_memalign(pagesize, size); + MallocHook::InvokeNewHook(result, size); + return result; +} + +extern "C" void malloc_stats(void) { + do_malloc_stats(); +} + +extern "C" int mallopt(int cmd, int value) { + return do_mallopt(cmd, value); +} + +#ifdef HAVE_STRUCT_MALLINFO +extern "C" struct mallinfo mallinfo(void) { + return do_mallinfo(); +} +#endif + +//------------------------------------------------------------------- +// Some library routines on RedHat 9 allocate memory using malloc() +// and free it using __libc_free() (or vice-versa). Since we provide +// our own implementations of malloc/free, we need to make sure that +// the __libc_XXX variants (defined as part of glibc) also point to +// the same implementations. +//------------------------------------------------------------------- + +#if defined(__GLIBC__) +extern "C" { +#if COMPILER(GCC) && !defined(__MACH__) && defined(HAVE___ATTRIBUTE__) + // Potentially faster variants that use the gcc alias extension. + // Mach-O (Darwin) does not support weak aliases, hence the __MACH__ check. +# define ALIAS(x) __attribute__ ((weak, alias (x))) + void* __libc_malloc(size_t size) ALIAS("malloc"); + void __libc_free(void* ptr) ALIAS("free"); + void* __libc_realloc(void* ptr, size_t size) ALIAS("realloc"); + void* __libc_calloc(size_t n, size_t size) ALIAS("calloc"); + void __libc_cfree(void* ptr) ALIAS("cfree"); + void* __libc_memalign(size_t align, size_t s) ALIAS("memalign"); + void* __libc_valloc(size_t size) ALIAS("valloc"); + void* __libc_pvalloc(size_t size) ALIAS("pvalloc"); + int __posix_memalign(void** r, size_t a, size_t s) ALIAS("posix_memalign"); +# undef ALIAS +# else /* not __GNUC__ */ + // Portable wrappers + void* __libc_malloc(size_t size) { return malloc(size); } + void __libc_free(void* ptr) { free(ptr); } + void* __libc_realloc(void* ptr, size_t size) { return realloc(ptr, size); } + void* __libc_calloc(size_t n, size_t size) { return calloc(n, size); } + void __libc_cfree(void* ptr) { cfree(ptr); } + void* __libc_memalign(size_t align, size_t s) { return memalign(align, s); } + void* __libc_valloc(size_t size) { return valloc(size); } + void* __libc_pvalloc(size_t size) { return pvalloc(size); } + int __posix_memalign(void** r, size_t a, size_t s) { + return posix_memalign(r, a, s); + } +# endif /* __GNUC__ */ +} +#endif /* __GLIBC__ */ + +// Override __libc_memalign in libc on linux boxes specially. +// They have a bug in libc that causes them to (very rarely) allocate +// with __libc_memalign() yet deallocate with free() and the +// definitions above don't catch it. +// This function is an exception to the rule of calling MallocHook method +// from the stack frame of the allocation function; +// heap-checker handles this special case explicitly. +static void *MemalignOverride(size_t align, size_t size, const void *caller) + __THROW { + void* result = do_memalign(align, size); + MallocHook::InvokeNewHook(result, size); + return result; +} +void *(*__memalign_hook)(size_t, size_t, const void *) = MemalignOverride; + +#endif + +#ifdef WTF_CHANGES +void releaseFastMallocFreeMemory() +{ + // Flush free pages in the current thread cache back to the page heap. + // Low watermark mechanism in Scavenge() prevents full return on the first pass. + // The second pass flushes everything. + if (TCMalloc_ThreadCache* threadCache = TCMalloc_ThreadCache::GetCacheIfPresent()) { + threadCache->Scavenge(); + threadCache->Scavenge(); + } + + SpinLockHolder h(&pageheap_lock); + pageheap->ReleaseFreePages(); +} + +FastMallocStatistics fastMallocStatistics() +{ + FastMallocStatistics statistics; + + SpinLockHolder lockHolder(&pageheap_lock); + statistics.reservedVMBytes = static_cast<size_t>(pageheap->SystemBytes()); + statistics.committedVMBytes = statistics.reservedVMBytes - pageheap->ReturnedBytes(); + + statistics.freeListBytes = 0; + for (unsigned cl = 0; cl < kNumClasses; ++cl) { + const int length = central_cache[cl].length(); + const int tc_length = central_cache[cl].tc_length(); + + statistics.freeListBytes += ByteSizeForClass(cl) * (length + tc_length); + } + for (TCMalloc_ThreadCache* threadCache = thread_heaps; threadCache ; threadCache = threadCache->next_) + statistics.freeListBytes += threadCache->Size(); + + return statistics; +} + +size_t fastMallocSize(const void* ptr) +{ +#if ENABLE(WTF_MALLOC_VALIDATION) + return Internal::fastMallocValidationHeader(const_cast<void*>(ptr))->m_size; +#else + const PageID p = reinterpret_cast<uintptr_t>(ptr) >> kPageShift; + Span* span = pageheap->GetDescriptorEnsureSafe(p); + + if (!span || span->free) + return 0; + + for (void* free = span->objects; free != NULL; free = *((void**) free)) { + if (ptr == free) + return 0; + } + + if (size_t cl = span->sizeclass) + return ByteSizeForClass(cl); + + return span->length << kPageShift; +#endif +} + +#if OS(DARWIN) + +class FreeObjectFinder { + const RemoteMemoryReader& m_reader; + HashSet<void*> m_freeObjects; + +public: + FreeObjectFinder(const RemoteMemoryReader& reader) : m_reader(reader) { } + + void visit(void* ptr) { m_freeObjects.add(ptr); } + bool isFreeObject(void* ptr) const { return m_freeObjects.contains(ptr); } + bool isFreeObject(vm_address_t ptr) const { return isFreeObject(reinterpret_cast<void*>(ptr)); } + size_t freeObjectCount() const { return m_freeObjects.size(); } + + void findFreeObjects(TCMalloc_ThreadCache* threadCache) + { + for (; threadCache; threadCache = (threadCache->next_ ? m_reader(threadCache->next_) : 0)) + threadCache->enumerateFreeObjects(*this, m_reader); + } + + void findFreeObjects(TCMalloc_Central_FreeListPadded* centralFreeList, size_t numSizes, TCMalloc_Central_FreeListPadded* remoteCentralFreeList) + { + for (unsigned i = 0; i < numSizes; i++) + centralFreeList[i].enumerateFreeObjects(*this, m_reader, remoteCentralFreeList + i); + } +}; + +class PageMapFreeObjectFinder { + const RemoteMemoryReader& m_reader; + FreeObjectFinder& m_freeObjectFinder; + +public: + PageMapFreeObjectFinder(const RemoteMemoryReader& reader, FreeObjectFinder& freeObjectFinder) + : m_reader(reader) + , m_freeObjectFinder(freeObjectFinder) + { } + + int visit(void* ptr) const + { + if (!ptr) + return 1; + + Span* span = m_reader(reinterpret_cast<Span*>(ptr)); + if (!span) + return 1; + + if (span->free) { + void* ptr = reinterpret_cast<void*>(span->start << kPageShift); + m_freeObjectFinder.visit(ptr); + } else if (span->sizeclass) { + // Walk the free list of the small-object span, keeping track of each object seen + for (void* nextObject = span->objects; nextObject; nextObject = m_reader.nextEntryInLinkedList(reinterpret_cast<void**>(nextObject))) + m_freeObjectFinder.visit(nextObject); + } + return span->length; + } +}; + +class PageMapMemoryUsageRecorder { + task_t m_task; + void* m_context; + unsigned m_typeMask; + vm_range_recorder_t* m_recorder; + const RemoteMemoryReader& m_reader; + const FreeObjectFinder& m_freeObjectFinder; + + HashSet<void*> m_seenPointers; + Vector<Span*> m_coalescedSpans; + +public: + PageMapMemoryUsageRecorder(task_t task, void* context, unsigned typeMask, vm_range_recorder_t* recorder, const RemoteMemoryReader& reader, const FreeObjectFinder& freeObjectFinder) + : m_task(task) + , m_context(context) + , m_typeMask(typeMask) + , m_recorder(recorder) + , m_reader(reader) + , m_freeObjectFinder(freeObjectFinder) + { } + + ~PageMapMemoryUsageRecorder() + { + ASSERT(!m_coalescedSpans.size()); + } + + void recordPendingRegions() + { + Span* lastSpan = m_coalescedSpans[m_coalescedSpans.size() - 1]; + vm_range_t ptrRange = { m_coalescedSpans[0]->start << kPageShift, 0 }; + ptrRange.size = (lastSpan->start << kPageShift) - ptrRange.address + (lastSpan->length * kPageSize); + + // Mark the memory region the spans represent as a candidate for containing pointers + if (m_typeMask & MALLOC_PTR_REGION_RANGE_TYPE) + (*m_recorder)(m_task, m_context, MALLOC_PTR_REGION_RANGE_TYPE, &ptrRange, 1); + + if (!(m_typeMask & MALLOC_PTR_IN_USE_RANGE_TYPE)) { + m_coalescedSpans.clear(); + return; + } + + Vector<vm_range_t, 1024> allocatedPointers; + for (size_t i = 0; i < m_coalescedSpans.size(); ++i) { + Span *theSpan = m_coalescedSpans[i]; + if (theSpan->free) + continue; + + vm_address_t spanStartAddress = theSpan->start << kPageShift; + vm_size_t spanSizeInBytes = theSpan->length * kPageSize; + + if (!theSpan->sizeclass) { + // If it's an allocated large object span, mark it as in use + if (!m_freeObjectFinder.isFreeObject(spanStartAddress)) + allocatedPointers.append((vm_range_t){spanStartAddress, spanSizeInBytes}); + } else { + const size_t objectSize = ByteSizeForClass(theSpan->sizeclass); + + // Mark each allocated small object within the span as in use + const vm_address_t endOfSpan = spanStartAddress + spanSizeInBytes; + for (vm_address_t object = spanStartAddress; object + objectSize <= endOfSpan; object += objectSize) { + if (!m_freeObjectFinder.isFreeObject(object)) + allocatedPointers.append((vm_range_t){object, objectSize}); + } + } + } + + (*m_recorder)(m_task, m_context, MALLOC_PTR_IN_USE_RANGE_TYPE, allocatedPointers.data(), allocatedPointers.size()); + + m_coalescedSpans.clear(); + } + + int visit(void* ptr) + { + if (!ptr) + return 1; + + Span* span = m_reader(reinterpret_cast<Span*>(ptr)); + if (!span || !span->start) + return 1; + + if (m_seenPointers.contains(ptr)) + return span->length; + m_seenPointers.add(ptr); + + if (!m_coalescedSpans.size()) { + m_coalescedSpans.append(span); + return span->length; + } + + Span* previousSpan = m_coalescedSpans[m_coalescedSpans.size() - 1]; + vm_address_t previousSpanStartAddress = previousSpan->start << kPageShift; + vm_size_t previousSpanSizeInBytes = previousSpan->length * kPageSize; + + // If the new span is adjacent to the previous span, do nothing for now. + vm_address_t spanStartAddress = span->start << kPageShift; + if (spanStartAddress == previousSpanStartAddress + previousSpanSizeInBytes) { + m_coalescedSpans.append(span); + return span->length; + } + + // New span is not adjacent to previous span, so record the spans coalesced so far. + recordPendingRegions(); + m_coalescedSpans.append(span); + + return span->length; + } +}; + +class AdminRegionRecorder { + task_t m_task; + void* m_context; + unsigned m_typeMask; + vm_range_recorder_t* m_recorder; + const RemoteMemoryReader& m_reader; + + Vector<vm_range_t, 1024> m_pendingRegions; + +public: + AdminRegionRecorder(task_t task, void* context, unsigned typeMask, vm_range_recorder_t* recorder, const RemoteMemoryReader& reader) + : m_task(task) + , m_context(context) + , m_typeMask(typeMask) + , m_recorder(recorder) + , m_reader(reader) + { } + + void recordRegion(vm_address_t ptr, size_t size) + { + if (m_typeMask & MALLOC_ADMIN_REGION_RANGE_TYPE) + m_pendingRegions.append((vm_range_t){ ptr, size }); + } + + void visit(void *ptr, size_t size) + { + recordRegion(reinterpret_cast<vm_address_t>(ptr), size); + } + + void recordPendingRegions() + { + if (m_pendingRegions.size()) { + (*m_recorder)(m_task, m_context, MALLOC_ADMIN_REGION_RANGE_TYPE, m_pendingRegions.data(), m_pendingRegions.size()); + m_pendingRegions.clear(); + } + } + + ~AdminRegionRecorder() + { + ASSERT(!m_pendingRegions.size()); + } +}; + +kern_return_t FastMallocZone::enumerate(task_t task, void* context, unsigned typeMask, vm_address_t zoneAddress, memory_reader_t reader, vm_range_recorder_t recorder) +{ + RemoteMemoryReader memoryReader(task, reader); + + InitSizeClasses(); + + FastMallocZone* mzone = memoryReader(reinterpret_cast<FastMallocZone*>(zoneAddress)); + TCMalloc_PageHeap* pageHeap = memoryReader(mzone->m_pageHeap); + TCMalloc_ThreadCache** threadHeapsPointer = memoryReader(mzone->m_threadHeaps); + TCMalloc_ThreadCache* threadHeaps = memoryReader(*threadHeapsPointer); + + TCMalloc_Central_FreeListPadded* centralCaches = memoryReader(mzone->m_centralCaches, sizeof(TCMalloc_Central_FreeListPadded) * kNumClasses); + + FreeObjectFinder finder(memoryReader); + finder.findFreeObjects(threadHeaps); + finder.findFreeObjects(centralCaches, kNumClasses, mzone->m_centralCaches); + + TCMalloc_PageHeap::PageMap* pageMap = &pageHeap->pagemap_; + PageMapFreeObjectFinder pageMapFinder(memoryReader, finder); + pageMap->visitValues(pageMapFinder, memoryReader); + + PageMapMemoryUsageRecorder usageRecorder(task, context, typeMask, recorder, memoryReader, finder); + pageMap->visitValues(usageRecorder, memoryReader); + usageRecorder.recordPendingRegions(); + + AdminRegionRecorder adminRegionRecorder(task, context, typeMask, recorder, memoryReader); + pageMap->visitAllocations(adminRegionRecorder, memoryReader); + + PageHeapAllocator<Span>* spanAllocator = memoryReader(mzone->m_spanAllocator); + PageHeapAllocator<TCMalloc_ThreadCache>* pageHeapAllocator = memoryReader(mzone->m_pageHeapAllocator); + + spanAllocator->recordAdministrativeRegions(adminRegionRecorder, memoryReader); + pageHeapAllocator->recordAdministrativeRegions(adminRegionRecorder, memoryReader); + + adminRegionRecorder.recordPendingRegions(); + + return 0; +} + +size_t FastMallocZone::size(malloc_zone_t*, const void*) +{ + return 0; +} + +void* FastMallocZone::zoneMalloc(malloc_zone_t*, size_t) +{ + return 0; +} + +void* FastMallocZone::zoneCalloc(malloc_zone_t*, size_t, size_t) +{ + return 0; +} + +void FastMallocZone::zoneFree(malloc_zone_t*, void* ptr) +{ + // Due to <rdar://problem/5671357> zoneFree may be called by the system free even if the pointer + // is not in this zone. When this happens, the pointer being freed was not allocated by any + // zone so we need to print a useful error for the application developer. + malloc_printf("*** error for object %p: pointer being freed was not allocated\n", ptr); +} + +void* FastMallocZone::zoneRealloc(malloc_zone_t*, void*, size_t) +{ + return 0; +} + + +#undef malloc +#undef free +#undef realloc +#undef calloc + +extern "C" { +malloc_introspection_t jscore_fastmalloc_introspection = { &FastMallocZone::enumerate, &FastMallocZone::goodSize, &FastMallocZone::check, &FastMallocZone::print, + &FastMallocZone::log, &FastMallocZone::forceLock, &FastMallocZone::forceUnlock, &FastMallocZone::statistics + +#if !defined(BUILDING_ON_LEOPARD) || OS(IOS) + , 0 // zone_locked will not be called on the zone unless it advertises itself as version five or higher. +#endif +#if !defined(BUILDING_ON_LEOPARD) && !defined(BUILDING_ON_SNOW_LEOPARD) || OS(IOS) + , 0, 0, 0, 0 // These members will not be used unless the zone advertises itself as version seven or higher. +#endif + + }; +} + +FastMallocZone::FastMallocZone(TCMalloc_PageHeap* pageHeap, TCMalloc_ThreadCache** threadHeaps, TCMalloc_Central_FreeListPadded* centralCaches, PageHeapAllocator<Span>* spanAllocator, PageHeapAllocator<TCMalloc_ThreadCache>* pageHeapAllocator) + : m_pageHeap(pageHeap) + , m_threadHeaps(threadHeaps) + , m_centralCaches(centralCaches) + , m_spanAllocator(spanAllocator) + , m_pageHeapAllocator(pageHeapAllocator) +{ + memset(&m_zone, 0, sizeof(m_zone)); + m_zone.version = 4; + m_zone.zone_name = "JavaScriptCore FastMalloc"; + m_zone.size = &FastMallocZone::size; + m_zone.malloc = &FastMallocZone::zoneMalloc; + m_zone.calloc = &FastMallocZone::zoneCalloc; + m_zone.realloc = &FastMallocZone::zoneRealloc; + m_zone.free = &FastMallocZone::zoneFree; + m_zone.valloc = &FastMallocZone::zoneValloc; + m_zone.destroy = &FastMallocZone::zoneDestroy; + m_zone.introspect = &jscore_fastmalloc_introspection; + malloc_zone_register(&m_zone); +} + + +void FastMallocZone::init() +{ + static FastMallocZone zone(pageheap, &thread_heaps, static_cast<TCMalloc_Central_FreeListPadded*>(central_cache), &span_allocator, &threadheap_allocator); +} + +#endif // OS(DARWIN) + +} // namespace WTF +#endif // WTF_CHANGES + +#endif // FORCE_SYSTEM_MALLOC diff --git a/Source/JavaScriptCore/wtf/FastMalloc.h b/Source/JavaScriptCore/wtf/FastMalloc.h new file mode 100644 index 000000000..b4f74e47b --- /dev/null +++ b/Source/JavaScriptCore/wtf/FastMalloc.h @@ -0,0 +1,281 @@ +/* + * Copyright (C) 2005, 2006, 2007, 2008, 2009 Apple Inc. All rights reserved. + * + * This library 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 library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#ifndef WTF_FastMalloc_h +#define WTF_FastMalloc_h + +#include "Platform.h" +#include "PossiblyNull.h" +#include <stdlib.h> +#include <new> + +namespace WTF { + + // These functions call CRASH() if an allocation fails. + void* fastMalloc(size_t); + void* fastZeroedMalloc(size_t); + void* fastCalloc(size_t numElements, size_t elementSize); + void* fastRealloc(void*, size_t); + char* fastStrDup(const char*); + size_t fastMallocSize(const void*); + + struct TryMallocReturnValue { + TryMallocReturnValue(void* data) + : m_data(data) + { + } + TryMallocReturnValue(const TryMallocReturnValue& source) + : m_data(source.m_data) + { + source.m_data = 0; + } + ~TryMallocReturnValue() { ASSERT(!m_data); } + template <typename T> bool getValue(T& data) WARN_UNUSED_RETURN; + template <typename T> operator PossiblyNull<T>() + { + T value; + getValue(value); + return PossiblyNull<T>(value); + } + private: + mutable void* m_data; + }; + + template <typename T> bool TryMallocReturnValue::getValue(T& data) + { + union u { void* data; T target; } res; + res.data = m_data; + data = res.target; + bool returnValue = !!m_data; + m_data = 0; + return returnValue; + } + + TryMallocReturnValue tryFastMalloc(size_t n); + TryMallocReturnValue tryFastZeroedMalloc(size_t n); + TryMallocReturnValue tryFastCalloc(size_t n_elements, size_t element_size); + TryMallocReturnValue tryFastRealloc(void* p, size_t n); + + void fastFree(void*); + +#ifndef NDEBUG + void fastMallocForbid(); + void fastMallocAllow(); +#endif + + void releaseFastMallocFreeMemory(); + + struct FastMallocStatistics { + size_t reservedVMBytes; + size_t committedVMBytes; + size_t freeListBytes; + }; + FastMallocStatistics fastMallocStatistics(); + + // This defines a type which holds an unsigned integer and is the same + // size as the minimally aligned memory allocation. + typedef unsigned long long AllocAlignmentInteger; + + namespace Internal { + enum AllocType { // Start with an unusual number instead of zero, because zero is common. + AllocTypeMalloc = 0x375d6750, // Encompasses fastMalloc, fastZeroedMalloc, fastCalloc, fastRealloc. + AllocTypeClassNew, // Encompasses class operator new from FastAllocBase. + AllocTypeClassNewArray, // Encompasses class operator new[] from FastAllocBase. + AllocTypeFastNew, // Encompasses fastNew. + AllocTypeFastNewArray, // Encompasses fastNewArray. + AllocTypeNew, // Encompasses global operator new. + AllocTypeNewArray // Encompasses global operator new[]. + }; + + enum { + ValidationPrefix = 0xf00df00d, + ValidationSuffix = 0x0badf00d + }; + + typedef unsigned ValidationTag; + + struct ValidationHeader { + AllocType m_type; + unsigned m_size; + ValidationTag m_prefix; + unsigned m_alignment; + }; + + static const int ValidationBufferSize = sizeof(ValidationHeader) + sizeof(ValidationTag); + } + +#if ENABLE(WTF_MALLOC_VALIDATION) + + // Malloc validation is a scheme whereby a tag is attached to an + // allocation which identifies how it was originally allocated. + // This allows us to verify that the freeing operation matches the + // allocation operation. If memory is allocated with operator new[] + // but freed with free or delete, this system would detect that. + // In the implementation here, the tag is an integer prepended to + // the allocation memory which is assigned one of the AllocType + // enumeration values. An alternative implementation of this + // scheme could store the tag somewhere else or ignore it. + // Users of FastMalloc don't need to know or care how this tagging + // is implemented. + + namespace Internal { + + // Handle a detected alloc/free mismatch. By default this calls CRASH(). + void fastMallocMatchFailed(void* p); + + inline ValidationHeader* fastMallocValidationHeader(void* p) + { + return reinterpret_cast<ValidationHeader*>(static_cast<char*>(p) - sizeof(ValidationHeader)); + } + + inline ValidationTag* fastMallocValidationSuffix(void* p) + { + ValidationHeader* header = fastMallocValidationHeader(p); + if (header->m_prefix != static_cast<unsigned>(ValidationPrefix)) + fastMallocMatchFailed(p); + + return reinterpret_cast<ValidationTag*>(static_cast<char*>(p) + header->m_size); + } + + // Return the AllocType tag associated with the allocated block p. + inline AllocType fastMallocMatchValidationType(void* p) + { + return fastMallocValidationHeader(p)->m_type; + } + + // Set the AllocType tag to be associaged with the allocated block p. + inline void setFastMallocMatchValidationType(void* p, AllocType allocType) + { + fastMallocValidationHeader(p)->m_type = allocType; + } + + } // namespace Internal + + // This is a higher level function which is used by FastMalloc-using code. + inline void fastMallocMatchValidateMalloc(void* p, Internal::AllocType allocType) + { + if (!p) + return; + + Internal::setFastMallocMatchValidationType(p, allocType); + } + + // This is a higher level function which is used by FastMalloc-using code. + inline void fastMallocMatchValidateFree(void* p, Internal::AllocType) + { + if (!p) + return; + + Internal::ValidationHeader* header = Internal::fastMallocValidationHeader(p); + if (header->m_prefix != static_cast<unsigned>(Internal::ValidationPrefix)) + Internal::fastMallocMatchFailed(p); + + if (*Internal::fastMallocValidationSuffix(p) != Internal::ValidationSuffix) + Internal::fastMallocMatchFailed(p); + + Internal::setFastMallocMatchValidationType(p, Internal::AllocTypeMalloc); // Set it to this so that fastFree thinks it's OK. + } + + inline void fastMallocValidate(void* p) + { + if (!p) + return; + + Internal::ValidationHeader* header = Internal::fastMallocValidationHeader(p); + if (header->m_prefix != static_cast<unsigned>(Internal::ValidationPrefix)) + Internal::fastMallocMatchFailed(p); + + if (*Internal::fastMallocValidationSuffix(p) != Internal::ValidationSuffix) + Internal::fastMallocMatchFailed(p); + } + +#else + + inline void fastMallocMatchValidateMalloc(void*, Internal::AllocType) + { + } + + inline void fastMallocMatchValidateFree(void*, Internal::AllocType) + { + } + +#endif + +} // namespace WTF + +using WTF::fastCalloc; +using WTF::fastFree; +using WTF::fastMalloc; +using WTF::fastMallocSize; +using WTF::fastRealloc; +using WTF::fastStrDup; +using WTF::fastZeroedMalloc; +using WTF::tryFastCalloc; +using WTF::tryFastMalloc; +using WTF::tryFastRealloc; +using WTF::tryFastZeroedMalloc; + +#ifndef NDEBUG +using WTF::fastMallocForbid; +using WTF::fastMallocAllow; +#endif + +#if COMPILER(GCC) && OS(DARWIN) +#define WTF_PRIVATE_INLINE __private_extern__ inline __attribute__((always_inline)) +#elif COMPILER(GCC) +#define WTF_PRIVATE_INLINE inline __attribute__((always_inline)) +#elif COMPILER(MSVC) || COMPILER(RVCT) +#define WTF_PRIVATE_INLINE __forceinline +#else +#define WTF_PRIVATE_INLINE inline +#endif + +#if !defined(_CRTDBG_MAP_ALLOC) && !(defined(USE_SYSTEM_MALLOC) && USE_SYSTEM_MALLOC) + +// The nothrow functions here are actually not all that helpful, because fastMalloc will +// call CRASH() rather than returning 0, and returning 0 is what nothrow is all about. +// But since WebKit code never uses exceptions or nothrow at all, this is probably OK. +// Long term we will adopt FastAllocBase.h everywhere, and and replace this with +// debug-only code to make sure we don't use the system malloc via the default operator +// new by accident. + +#if ENABLE(GLOBAL_FASTMALLOC_NEW) + +#if COMPILER(MSVC) +#pragma warning(push) +#pragma warning(disable: 4290) // Disable the C++ exception specification ignored warning. +#endif +WTF_PRIVATE_INLINE void* operator new(size_t size) throw (std::bad_alloc) { return fastMalloc(size); } +WTF_PRIVATE_INLINE void* operator new(size_t size, const std::nothrow_t&) throw() { return fastMalloc(size); } +WTF_PRIVATE_INLINE void operator delete(void* p) throw() { fastFree(p); } +WTF_PRIVATE_INLINE void operator delete(void* p, const std::nothrow_t&) throw() { fastFree(p); } +WTF_PRIVATE_INLINE void* operator new[](size_t size) throw (std::bad_alloc) { return fastMalloc(size); } +WTF_PRIVATE_INLINE void* operator new[](size_t size, const std::nothrow_t&) throw() { return fastMalloc(size); } +WTF_PRIVATE_INLINE void operator delete[](void* p) throw() { fastFree(p); } +WTF_PRIVATE_INLINE void operator delete[](void* p, const std::nothrow_t&) throw() { fastFree(p); } +#if COMPILER(MSVC) +#pragma warning(pop) +#endif + +#endif + +#endif + +#endif /* WTF_FastMalloc_h */ diff --git a/Source/JavaScriptCore/wtf/FixedArray.h b/Source/JavaScriptCore/wtf/FixedArray.h new file mode 100644 index 000000000..c67d18cda --- /dev/null +++ b/Source/JavaScriptCore/wtf/FixedArray.h @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2010 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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 APPLE INC. AND ITS 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 APPLE INC. OR ITS 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. + */ + +#ifndef FixedArray_h +#define FixedArray_h + +#include <wtf/Assertions.h> + +namespace WTF { + +template <typename T, size_t Size> class FixedArray { +public: + T& operator[](size_t i) + { + ASSERT(i < Size); + return m_data[i]; + } + + const T& operator[](size_t i) const + { + ASSERT(i < Size); + return m_data[i]; + } + + T* data() { return m_data; } + size_t size() const { return Size; } + +private: + T m_data[Size]; +}; + +} // namespace WTF + +using WTF::FixedArray; + +#endif // FixedArray_h diff --git a/Source/JavaScriptCore/wtf/Float32Array.h b/Source/JavaScriptCore/wtf/Float32Array.h new file mode 100644 index 000000000..230a768aa --- /dev/null +++ b/Source/JavaScriptCore/wtf/Float32Array.h @@ -0,0 +1,108 @@ +/* + * Copyright (C) 2009 Apple Inc. All rights reserved. + * Copyright (C) 2009 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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 APPLE COMPUTER, INC. ``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 APPLE COMPUTER, INC. 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. + */ + +#ifndef Float32Array_h +#define Float32Array_h + +#include "TypedArrayBase.h" +#include <wtf/MathExtras.h> + +namespace WTF { + +class Float32Array : public TypedArrayBase<float> { +public: + static inline PassRefPtr<Float32Array> create(unsigned length); + static inline PassRefPtr<Float32Array> create(const float* array, unsigned length); + static inline PassRefPtr<Float32Array> create(PassRefPtr<ArrayBuffer>, unsigned byteOffset, unsigned length); + + // Can’t use "using" here due to a bug in the RVCT compiler. + bool set(TypedArrayBase<float>* array, unsigned offset) { return TypedArrayBase<float>::set(array, offset); } + + void set(unsigned index, double value) + { + if (index >= TypedArrayBase<float>::m_length) + return; + TypedArrayBase<float>::data()[index] = static_cast<float>(value); + } + + // Invoked by the indexed getter. Does not perform range checks; caller + // is responsible for doing so and returning undefined as necessary. + float item(unsigned index) const + { + ASSERT(index < TypedArrayBase<float>::m_length); + float result = TypedArrayBase<float>::data()[index]; + return result; + } + + inline PassRefPtr<Float32Array> subarray(int start) const; + inline PassRefPtr<Float32Array> subarray(int start, int end) const; + +private: + inline Float32Array(PassRefPtr<ArrayBuffer>, + unsigned byteOffset, + unsigned length); + // Make constructor visible to superclass. + friend class TypedArrayBase<float>; + + // Overridden from ArrayBufferView. + virtual bool isFloatArray() const { return true; } +}; + +PassRefPtr<Float32Array> Float32Array::create(unsigned length) +{ + return TypedArrayBase<float>::create<Float32Array>(length); +} + +PassRefPtr<Float32Array> Float32Array::create(const float* array, unsigned length) +{ + return TypedArrayBase<float>::create<Float32Array>(array, length); +} + +PassRefPtr<Float32Array> Float32Array::create(PassRefPtr<ArrayBuffer> buffer, unsigned byteOffset, unsigned length) +{ + return TypedArrayBase<float>::create<Float32Array>(buffer, byteOffset, length); +} + +Float32Array::Float32Array(PassRefPtr<ArrayBuffer> buffer, unsigned byteOffset, unsigned length) + : TypedArrayBase<float>(buffer, byteOffset, length) +{ +} + +PassRefPtr<Float32Array> Float32Array::subarray(int start) const +{ + return subarray(start, length()); +} + +PassRefPtr<Float32Array> Float32Array::subarray(int start, int end) const +{ + return subarrayImpl<Float32Array>(start, end); +} + +} // namespace WTF + +using WTF::Float32Array; + +#endif // Float32Array_h diff --git a/Source/JavaScriptCore/wtf/Float64Array.h b/Source/JavaScriptCore/wtf/Float64Array.h new file mode 100644 index 000000000..46713556c --- /dev/null +++ b/Source/JavaScriptCore/wtf/Float64Array.h @@ -0,0 +1,108 @@ +/* + * Copyright (C) 2011 Apple Inc. All rights reserved. + * Copyright (C) 2011 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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 APPLE COMPUTER, INC. ``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 APPLE COMPUTER, INC. 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. + */ + +#ifndef Float64Array_h +#define Float64Array_h + +#include "TypedArrayBase.h" +#include <wtf/MathExtras.h> + +namespace WTF { + +class Float64Array : public TypedArrayBase<double> { +public: + static inline PassRefPtr<Float64Array> create(unsigned length); + static inline PassRefPtr<Float64Array> create(const double* array, unsigned length); + static inline PassRefPtr<Float64Array> create(PassRefPtr<ArrayBuffer>, unsigned byteOffset, unsigned length); + + // Can’t use "using" here due to a bug in the RVCT compiler. + bool set(TypedArrayBase<double>* array, unsigned offset) { return TypedArrayBase<double>::set(array, offset); } + + void set(unsigned index, double value) + { + if (index >= TypedArrayBase<double>::m_length) + return; + TypedArrayBase<double>::data()[index] = static_cast<double>(value); + } + + // Invoked by the indexed getter. Does not perform range checks; caller + // is responsible for doing so and returning undefined as necessary. + double item(unsigned index) const + { + ASSERT(index < TypedArrayBase<double>::m_length); + double result = TypedArrayBase<double>::data()[index]; + return result; + } + + inline PassRefPtr<Float64Array> subarray(int start) const; + inline PassRefPtr<Float64Array> subarray(int start, int end) const; + +private: + inline Float64Array(PassRefPtr<ArrayBuffer>, + unsigned byteOffset, + unsigned length); + // Make constructor visible to superclass. + friend class TypedArrayBase<double>; + + // Overridden from ArrayBufferView. + virtual bool isDoubleArray() const { return true; } +}; + +PassRefPtr<Float64Array> Float64Array::create(unsigned length) +{ + return TypedArrayBase<double>::create<Float64Array>(length); +} + +PassRefPtr<Float64Array> Float64Array::create(const double* array, unsigned length) +{ + return TypedArrayBase<double>::create<Float64Array>(array, length); +} + +PassRefPtr<Float64Array> Float64Array::create(PassRefPtr<ArrayBuffer> buffer, unsigned byteOffset, unsigned length) +{ + return TypedArrayBase<double>::create<Float64Array>(buffer, byteOffset, length); +} + +Float64Array::Float64Array(PassRefPtr<ArrayBuffer> buffer, unsigned byteOffset, unsigned length) + : TypedArrayBase<double>(buffer, byteOffset, length) +{ +} + +PassRefPtr<Float64Array> Float64Array::subarray(int start) const +{ + return subarray(start, length()); +} + +PassRefPtr<Float64Array> Float64Array::subarray(int start, int end) const +{ + return subarrayImpl<Float64Array>(start, end); +} + +} // namespace WTF + +using WTF::Float64Array; + +#endif // Float64Array_h diff --git a/Source/JavaScriptCore/wtf/Forward.h b/Source/JavaScriptCore/wtf/Forward.h new file mode 100644 index 000000000..3c97849cc --- /dev/null +++ b/Source/JavaScriptCore/wtf/Forward.h @@ -0,0 +1,88 @@ +/* + * Copyright (C) 2006, 2009, 2011 Apple Inc. All rights reserved. + * + * This library 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 library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#ifndef WTF_Forward_h +#define WTF_Forward_h + +#include <stddef.h> + +namespace WTF { + template<typename T> class Function; + template<typename T> class ListRefPtr; + template<typename T> class OwnArrayPtr; + template<typename T> class OwnPtr; + template<typename T> class PassOwnArrayPtr; + template<typename T> class PassOwnPtr; + template<typename T> class PassRefPtr; + template<typename T> class RefPtr; + template<typename T, size_t inlineCapacity> class Vector; + + class ArrayBuffer; + class ArrayBufferView; + class AtomicString; + class AtomicStringImpl; + class CString; + class Decoder; + class Encoder; + class Float32Array; + class Float64Array; + class Int8Array; + class Int16Array; + class Int32Array; + class String; + template <typename T> class StringBuffer; + class StringBuilder; + class StringImpl; + class Uint8Array; + class Uint16Array; + class Uint32Array; +} + +using WTF::Function; +using WTF::ListRefPtr; +using WTF::OwnArrayPtr; +using WTF::OwnPtr; +using WTF::PassOwnArrayPtr; +using WTF::PassOwnPtr; +using WTF::PassRefPtr; +using WTF::RefPtr; +using WTF::Vector; + +using WTF::ArrayBuffer; +using WTF::ArrayBufferView; +using WTF::AtomicString; +using WTF::AtomicStringImpl; +using WTF::CString; +using WTF::Encoder; +using WTF::Decoder; +using WTF::Float32Array; +using WTF::Float64Array; +using WTF::Int8Array; +using WTF::Int16Array; +using WTF::Int32Array; +using WTF::String; +using WTF::StringBuffer; +using WTF::StringBuilder; +using WTF::StringImpl; +using WTF::Uint8Array; +using WTF::Uint16Array; +using WTF::Uint32Array; + +#endif // WTF_Forward_h diff --git a/Source/JavaScriptCore/wtf/Functional.h b/Source/JavaScriptCore/wtf/Functional.h new file mode 100644 index 000000000..86d6203cf --- /dev/null +++ b/Source/JavaScriptCore/wtf/Functional.h @@ -0,0 +1,638 @@ +/* + * Copyright (C) 2011 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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 APPLE INC. AND ITS 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 APPLE INC. OR ITS 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. + */ + +#ifndef WTF_Functional_h +#define WTF_Functional_h + +#include "Assertions.h" +#include "PassRefPtr.h" +#include "RefPtr.h" +#include "ThreadSafeRefCounted.h" + +#if PLATFORM(MAC) && COMPILER_SUPPORTS(BLOCKS) +#include <objc/objc-runtime.h> +#endif + +namespace WTF { + +// Functional.h provides a very simple way to bind a function pointer and arguments together into a function object +// that can be stored, copied and invoked, similar to how boost::bind and std::bind in C++11. + +// Helper class template to determine whether a given type has ref and deref member functions +// with the right type signature. +template<typename T> +class HasRefAndDeref { + typedef char YesType; + struct NoType { + char padding[8]; + }; + + struct BaseMixin { + void deref(); + void ref(); + }; + + struct Base : public T, public BaseMixin { }; + + template<typename U, U> struct + TypeChecker { }; + + template<typename U> + static NoType refCheck(U*, TypeChecker<void (BaseMixin::*)(), &U::ref>* = 0); + static YesType refCheck(...); + + template<typename U> + static NoType derefCheck(U*, TypeChecker<void (BaseMixin::*)(), &U::deref>* = 0); + static YesType derefCheck(...); + +public: + static const bool value = sizeof(refCheck(static_cast<Base*>(0))) == sizeof(YesType) && sizeof(derefCheck(static_cast<Base*>(0))) == sizeof(YesType); +}; + +// A FunctionWrapper is a class template that can wrap a function pointer or a member function pointer and +// provide a unified interface for calling that function. +template<typename> +class FunctionWrapper; + +template<typename R> +class FunctionWrapper<R (*)()> { +public: + typedef R ResultType; + static const bool shouldRefFirstParameter = false; + + explicit FunctionWrapper(R (*function)()) + : m_function(function) + { + } + + R operator()() + { + return m_function(); + } + +private: + R (*m_function)(); +}; + +template<typename R, typename P1> +class FunctionWrapper<R (*)(P1)> { +public: + typedef R ResultType; + static const bool shouldRefFirstParameter = false; + + explicit FunctionWrapper(R (*function)(P1)) + : m_function(function) + { + } + + R operator()(P1 p1) + { + return m_function(p1); + } + +private: + R (*m_function)(P1); +}; + +template<typename R, typename P1, typename P2> +class FunctionWrapper<R (*)(P1, P2)> { +public: + typedef R ResultType; + static const bool shouldRefFirstParameter = false; + + explicit FunctionWrapper(R (*function)(P1, P2)) + : m_function(function) + { + } + + R operator()(P1 p1, P2 p2) + { + return m_function(p1, p2); + } + +private: + R (*m_function)(P1, P2); +}; + +template<typename R, typename C> +class FunctionWrapper<R (C::*)()> { +public: + typedef R ResultType; + static const bool shouldRefFirstParameter = HasRefAndDeref<C>::value; + + explicit FunctionWrapper(R (C::*function)()) + : m_function(function) + { + } + + R operator()(C* c) + { + return (c->*m_function)(); + } + +private: + R (C::*m_function)(); +}; + +template<typename R, typename C, typename P1> +class FunctionWrapper<R (C::*)(P1)> { +public: + typedef R ResultType; + static const bool shouldRefFirstParameter = HasRefAndDeref<C>::value; + + explicit FunctionWrapper(R (C::*function)(P1)) + : m_function(function) + { + } + + R operator()(C* c, P1 p1) + { + return (c->*m_function)(p1); + } + +private: + R (C::*m_function)(P1); +}; + +template<typename R, typename C, typename P1, typename P2> +class FunctionWrapper<R (C::*)(P1, P2)> { +public: + typedef R ResultType; + static const bool shouldRefFirstParameter = HasRefAndDeref<C>::value; + + explicit FunctionWrapper(R (C::*function)(P1, P2)) + : m_function(function) + { + } + + R operator()(C* c, P1 p1, P2 p2) + { + return (c->*m_function)(p1, p2); + } + +private: + R (C::*m_function)(P1, P2); +}; + +template<typename R, typename C, typename P1, typename P2, typename P3> +class FunctionWrapper<R (C::*)(P1, P2, P3)> { +public: + typedef R ResultType; + static const bool shouldRefFirstParameter = HasRefAndDeref<C>::value; + + explicit FunctionWrapper(R (C::*function)(P1, P2, P3)) + : m_function(function) + { + } + + R operator()(C* c, P1 p1, P2 p2, P3 p3) + { + return (c->*m_function)(p1, p2, p3); + } + +private: + R (C::*m_function)(P1, P2, P3); +}; + +template<typename R, typename C, typename P1, typename P2, typename P3, typename P4> +class FunctionWrapper<R (C::*)(P1, P2, P3, P4)> { +public: + typedef R ResultType; + static const bool shouldRefFirstParameter = HasRefAndDeref<C>::value; + + explicit FunctionWrapper(R (C::*function)(P1, P2, P3, P4)) + : m_function(function) + { + } + + R operator()(C* c, P1 p1, P2 p2, P3 p3, P4 p4) + { + return (c->*m_function)(p1, p2, p3, p4); + } + +private: + R (C::*m_function)(P1, P2, P3, P4); +}; + +template<typename R, typename C, typename P1, typename P2, typename P3, typename P4, typename P5> +class FunctionWrapper<R (C::*)(P1, P2, P3, P4, P5)> { +public: + typedef R ResultType; + static const bool shouldRefFirstParameter = HasRefAndDeref<C>::value; + + explicit FunctionWrapper(R (C::*function)(P1, P2, P3, P4, P5)) + : m_function(function) + { + } + + R operator()(C* c, P1 p1, P2 p2, P3 p3, P4 p4, P5 p5) + { + return (c->*m_function)(p1, p2, p3, p4, p5); + } + +private: + R (C::*m_function)(P1, P2, P3, P4, P5); +}; + +template<typename T, bool shouldRefAndDeref> struct RefAndDeref { + static void ref(T) { } + static void deref(T) { } +}; + +template<typename T> struct RefAndDeref<T*, true> { + static void ref(T* t) { t->ref(); } + static void deref(T* t) { t->deref(); } +}; + +template<typename T> struct ParamStorageTraits { + typedef T StorageType; + + static StorageType wrap(const T& value) { return value; } + static const T& unwrap(const StorageType& value) { return value; } +}; + +template<typename T> struct ParamStorageTraits<PassRefPtr<T> > { + typedef RefPtr<T> StorageType; + + static StorageType wrap(PassRefPtr<T> value) { return value; } + static T* unwrap(const StorageType& value) { return value.get(); } +}; + +template<typename T> struct ParamStorageTraits<RefPtr<T> > { + typedef RefPtr<T> StorageType; + + static StorageType wrap(RefPtr<T> value) { return value.release(); } + static T* unwrap(const StorageType& value) { return value.get(); } +}; + + +template<typename> class RetainPtr; + +template<typename T> struct ParamStorageTraits<RetainPtr<T> > { + typedef RetainPtr<T> StorageType; + + static StorageType wrap(const RetainPtr<T>& value) { return value; } + static typename RetainPtr<T>::PtrType unwrap(const StorageType& value) { return value.get(); } +}; + +class FunctionImplBase : public ThreadSafeRefCounted<FunctionImplBase> { +public: + virtual ~FunctionImplBase() { } +}; + +template<typename> +class FunctionImpl; + +template<typename R> +class FunctionImpl<R ()> : public FunctionImplBase { +public: + virtual R operator()() = 0; +}; + +template<typename FunctionWrapper, typename FunctionType> +class BoundFunctionImpl; + +template<typename FunctionWrapper, typename R> +class BoundFunctionImpl<FunctionWrapper, R ()> : public FunctionImpl<typename FunctionWrapper::ResultType ()> { +public: + explicit BoundFunctionImpl(FunctionWrapper functionWrapper) + : m_functionWrapper(functionWrapper) + { + } + + virtual R operator()() + { + return m_functionWrapper(); + } + +private: + FunctionWrapper m_functionWrapper; +}; + +template<typename FunctionWrapper, typename R, typename P1> +class BoundFunctionImpl<FunctionWrapper, R (P1)> : public FunctionImpl<typename FunctionWrapper::ResultType ()> { + +public: + BoundFunctionImpl(FunctionWrapper functionWrapper, const P1& p1) + : m_functionWrapper(functionWrapper) + , m_p1(ParamStorageTraits<P1>::wrap(p1)) + { + RefAndDeref<P1, FunctionWrapper::shouldRefFirstParameter>::ref(m_p1); + } + + ~BoundFunctionImpl() + { + RefAndDeref<P1, FunctionWrapper::shouldRefFirstParameter>::deref(m_p1); + } + + virtual R operator()() + { + return m_functionWrapper(ParamStorageTraits<P1>::unwrap(m_p1)); + } + +private: + FunctionWrapper m_functionWrapper; + typename ParamStorageTraits<P1>::StorageType m_p1; +}; + +template<typename FunctionWrapper, typename R, typename P1, typename P2> +class BoundFunctionImpl<FunctionWrapper, R (P1, P2)> : public FunctionImpl<typename FunctionWrapper::ResultType ()> { +public: + BoundFunctionImpl(FunctionWrapper functionWrapper, const P1& p1, const P2& p2) + : m_functionWrapper(functionWrapper) + , m_p1(ParamStorageTraits<P1>::wrap(p1)) + , m_p2(ParamStorageTraits<P2>::wrap(p2)) + { + RefAndDeref<P1, FunctionWrapper::shouldRefFirstParameter>::ref(m_p1); + } + + ~BoundFunctionImpl() + { + RefAndDeref<P1, FunctionWrapper::shouldRefFirstParameter>::deref(m_p1); + } + + virtual typename FunctionWrapper::ResultType operator()() + { + return m_functionWrapper(ParamStorageTraits<P1>::unwrap(m_p1), ParamStorageTraits<P2>::unwrap(m_p2)); + } + +private: + FunctionWrapper m_functionWrapper; + typename ParamStorageTraits<P1>::StorageType m_p1; + typename ParamStorageTraits<P2>::StorageType m_p2; +}; + +template<typename FunctionWrapper, typename R, typename P1, typename P2, typename P3> +class BoundFunctionImpl<FunctionWrapper, R (P1, P2, P3)> : public FunctionImpl<typename FunctionWrapper::ResultType ()> { +public: + BoundFunctionImpl(FunctionWrapper functionWrapper, const P1& p1, const P2& p2, const P3& p3) + : m_functionWrapper(functionWrapper) + , m_p1(ParamStorageTraits<P1>::wrap(p1)) + , m_p2(ParamStorageTraits<P2>::wrap(p2)) + , m_p3(ParamStorageTraits<P3>::wrap(p3)) + { + RefAndDeref<P1, FunctionWrapper::shouldRefFirstParameter>::ref(m_p1); + } + + ~BoundFunctionImpl() + { + RefAndDeref<P1, FunctionWrapper::shouldRefFirstParameter>::deref(m_p1); + } + + virtual typename FunctionWrapper::ResultType operator()() + { + return m_functionWrapper(ParamStorageTraits<P1>::unwrap(m_p1), ParamStorageTraits<P2>::unwrap(m_p2), ParamStorageTraits<P3>::unwrap(m_p3)); + } + +private: + FunctionWrapper m_functionWrapper; + typename ParamStorageTraits<P1>::StorageType m_p1; + typename ParamStorageTraits<P2>::StorageType m_p2; + typename ParamStorageTraits<P3>::StorageType m_p3; +}; + +template<typename FunctionWrapper, typename R, typename P1, typename P2, typename P3, typename P4> +class BoundFunctionImpl<FunctionWrapper, R (P1, P2, P3, P4)> : public FunctionImpl<typename FunctionWrapper::ResultType ()> { +public: + BoundFunctionImpl(FunctionWrapper functionWrapper, const P1& p1, const P2& p2, const P3& p3, const P4& p4) + : m_functionWrapper(functionWrapper) + , m_p1(ParamStorageTraits<P1>::wrap(p1)) + , m_p2(ParamStorageTraits<P2>::wrap(p2)) + , m_p3(ParamStorageTraits<P3>::wrap(p3)) + , m_p4(ParamStorageTraits<P4>::wrap(p4)) + { + RefAndDeref<P1, FunctionWrapper::shouldRefFirstParameter>::ref(m_p1); + } + + ~BoundFunctionImpl() + { + RefAndDeref<P1, FunctionWrapper::shouldRefFirstParameter>::deref(m_p1); + } + + virtual typename FunctionWrapper::ResultType operator()() + { + return m_functionWrapper(ParamStorageTraits<P1>::unwrap(m_p1), ParamStorageTraits<P2>::unwrap(m_p2), ParamStorageTraits<P3>::unwrap(m_p3), ParamStorageTraits<P4>::unwrap(m_p4)); + } + +private: + FunctionWrapper m_functionWrapper; + typename ParamStorageTraits<P1>::StorageType m_p1; + typename ParamStorageTraits<P2>::StorageType m_p2; + typename ParamStorageTraits<P3>::StorageType m_p3; + typename ParamStorageTraits<P4>::StorageType m_p4; +}; + +template<typename FunctionWrapper, typename R, typename P1, typename P2, typename P3, typename P4, typename P5> +class BoundFunctionImpl<FunctionWrapper, R (P1, P2, P3, P4, P5)> : public FunctionImpl<typename FunctionWrapper::ResultType ()> { +public: + BoundFunctionImpl(FunctionWrapper functionWrapper, const P1& p1, const P2& p2, const P3& p3, const P4& p4, const P5& p5) + : m_functionWrapper(functionWrapper) + , m_p1(ParamStorageTraits<P1>::wrap(p1)) + , m_p2(ParamStorageTraits<P2>::wrap(p2)) + , m_p3(ParamStorageTraits<P3>::wrap(p3)) + , m_p4(ParamStorageTraits<P4>::wrap(p4)) + , m_p5(ParamStorageTraits<P5>::wrap(p5)) + { + RefAndDeref<P1, FunctionWrapper::shouldRefFirstParameter>::ref(m_p1); + } + + ~BoundFunctionImpl() + { + RefAndDeref<P1, FunctionWrapper::shouldRefFirstParameter>::deref(m_p1); + } + + virtual typename FunctionWrapper::ResultType operator()() + { + return m_functionWrapper(ParamStorageTraits<P1>::unwrap(m_p1), ParamStorageTraits<P2>::unwrap(m_p2), ParamStorageTraits<P3>::unwrap(m_p3), ParamStorageTraits<P4>::unwrap(m_p4), ParamStorageTraits<P5>::unwrap(m_p5)); + } + +private: + FunctionWrapper m_functionWrapper; + typename ParamStorageTraits<P1>::StorageType m_p1; + typename ParamStorageTraits<P2>::StorageType m_p2; + typename ParamStorageTraits<P3>::StorageType m_p3; + typename ParamStorageTraits<P4>::StorageType m_p4; + typename ParamStorageTraits<P5>::StorageType m_p5; +}; + +template<typename FunctionWrapper, typename R, typename P1, typename P2, typename P3, typename P4, typename P5, typename P6> +class BoundFunctionImpl<FunctionWrapper, R (P1, P2, P3, P4, P5, P6)> : public FunctionImpl<typename FunctionWrapper::ResultType ()> { +public: + BoundFunctionImpl(FunctionWrapper functionWrapper, const P1& p1, const P2& p2, const P3& p3, const P4& p4, const P5& p5, const P6& p6) + : m_functionWrapper(functionWrapper) + , m_p1(ParamStorageTraits<P1>::wrap(p1)) + , m_p2(ParamStorageTraits<P2>::wrap(p2)) + , m_p3(ParamStorageTraits<P3>::wrap(p3)) + , m_p4(ParamStorageTraits<P4>::wrap(p4)) + , m_p5(ParamStorageTraits<P5>::wrap(p5)) + , m_p6(ParamStorageTraits<P6>::wrap(p6)) + { + RefAndDeref<P1, FunctionWrapper::shouldRefFirstParameter>::ref(m_p1); + } + + ~BoundFunctionImpl() + { + RefAndDeref<P1, FunctionWrapper::shouldRefFirstParameter>::deref(m_p1); + } + + virtual typename FunctionWrapper::ResultType operator()() + { + return m_functionWrapper(ParamStorageTraits<P1>::unwrap(m_p1), ParamStorageTraits<P2>::unwrap(m_p2), ParamStorageTraits<P3>::unwrap(m_p3), ParamStorageTraits<P4>::unwrap(m_p4), ParamStorageTraits<P5>::unwrap(m_p5), ParamStorageTraits<P6>::unwrap(m_p6)); + } + +private: + FunctionWrapper m_functionWrapper; + typename ParamStorageTraits<P1>::StorageType m_p1; + typename ParamStorageTraits<P2>::StorageType m_p2; + typename ParamStorageTraits<P3>::StorageType m_p3; + typename ParamStorageTraits<P4>::StorageType m_p4; + typename ParamStorageTraits<P5>::StorageType m_p5; + typename ParamStorageTraits<P6>::StorageType m_p6; +}; + +class FunctionBase { +public: + bool isNull() const + { + return !m_impl; + } + +protected: + FunctionBase() + { + } + + explicit FunctionBase(PassRefPtr<FunctionImplBase> impl) + : m_impl(impl) + { + } + + template<typename FunctionType> FunctionImpl<FunctionType>* impl() const + { + return static_cast<FunctionImpl<FunctionType>*>(m_impl.get()); + } + +private: + RefPtr<FunctionImplBase> m_impl; +}; + +template<typename> +class Function; + +template<typename R> +class Function<R ()> : public FunctionBase { +public: + Function() + { + } + + Function(PassRefPtr<FunctionImpl<R ()> > impl) + : FunctionBase(impl) + { + } + + R operator()() const + { + ASSERT(!isNull()); + + return impl<R ()>()->operator()(); + } + +#if PLATFORM(MAC) && COMPILER_SUPPORTS(BLOCKS) + typedef void (^BlockType)(); + operator BlockType() const + { + // Declare a RefPtr here so we'll be sure that the underlying FunctionImpl object's + // lifecycle is managed correctly. + RefPtr<FunctionImpl<R ()> > functionImpl = impl<R ()>(); + BlockType block = ^{ + functionImpl->operator()(); + }; + + // This is equivalent to: + // + // return [[block copy] autorelease]; + // + // We're using manual objc_msgSend calls here because we don't want to make the entire + // file Objective-C. It's useful to be able to implicitly convert a Function to + // a block even in C++ code, since that allows us to do things like: + // + // dispatch_async(queue, bind(...)); + // + id copiedBlock = objc_msgSend((id)block, sel_registerName("copy")); + id autoreleasedBlock = objc_msgSend(copiedBlock, sel_registerName("autorelease")); + return (BlockType)autoreleasedBlock; + } +#endif +}; + +template<typename FunctionType> +Function<typename FunctionWrapper<FunctionType>::ResultType ()> bind(FunctionType function) +{ + return Function<typename FunctionWrapper<FunctionType>::ResultType ()>(adoptRef(new BoundFunctionImpl<FunctionWrapper<FunctionType>, typename FunctionWrapper<FunctionType>::ResultType ()>(FunctionWrapper<FunctionType>(function)))); +} + +template<typename FunctionType, typename A1> +Function<typename FunctionWrapper<FunctionType>::ResultType ()> bind(FunctionType function, const A1& a1) +{ + return Function<typename FunctionWrapper<FunctionType>::ResultType ()>(adoptRef(new BoundFunctionImpl<FunctionWrapper<FunctionType>, typename FunctionWrapper<FunctionType>::ResultType (A1)>(FunctionWrapper<FunctionType>(function), a1))); +} + +template<typename FunctionType, typename A1, typename A2> +Function<typename FunctionWrapper<FunctionType>::ResultType ()> bind(FunctionType function, const A1& a1, const A2& a2) +{ + return Function<typename FunctionWrapper<FunctionType>::ResultType ()>(adoptRef(new BoundFunctionImpl<FunctionWrapper<FunctionType>, typename FunctionWrapper<FunctionType>::ResultType (A1, A2)>(FunctionWrapper<FunctionType>(function), a1, a2))); +} + +template<typename FunctionType, typename A1, typename A2, typename A3> +Function<typename FunctionWrapper<FunctionType>::ResultType ()> bind(FunctionType function, const A1& a1, const A2& a2, const A3& a3) +{ + return Function<typename FunctionWrapper<FunctionType>::ResultType ()>(adoptRef(new BoundFunctionImpl<FunctionWrapper<FunctionType>, typename FunctionWrapper<FunctionType>::ResultType (A1, A2, A3)>(FunctionWrapper<FunctionType>(function), a1, a2, a3))); +} + +template<typename FunctionType, typename A1, typename A2, typename A3, typename A4> +Function<typename FunctionWrapper<FunctionType>::ResultType ()> bind(FunctionType function, const A1& a1, const A2& a2, const A3& a3, const A4& a4) +{ + return Function<typename FunctionWrapper<FunctionType>::ResultType ()>(adoptRef(new BoundFunctionImpl<FunctionWrapper<FunctionType>, typename FunctionWrapper<FunctionType>::ResultType (A1, A2, A3, A4)>(FunctionWrapper<FunctionType>(function), a1, a2, a3, a4))); +} + +template<typename FunctionType, typename A1, typename A2, typename A3, typename A4, typename A5> +Function<typename FunctionWrapper<FunctionType>::ResultType ()> bind(FunctionType function, const A1& a1, const A2& a2, const A3& a3, const A4& a4, const A5& a5) +{ + return Function<typename FunctionWrapper<FunctionType>::ResultType ()>(adoptRef(new BoundFunctionImpl<FunctionWrapper<FunctionType>, typename FunctionWrapper<FunctionType>::ResultType (A1, A2, A3, A4, A5)>(FunctionWrapper<FunctionType>(function), a1, a2, a3, a4, a5))); +} + +template<typename FunctionType, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6> +Function<typename FunctionWrapper<FunctionType>::ResultType ()> bind(FunctionType function, const A1& a1, const A2& a2, const A3& a3, const A4& a4, const A5& a5, const A6& a6) +{ + return Function<typename FunctionWrapper<FunctionType>::ResultType ()>(adoptRef(new BoundFunctionImpl<FunctionWrapper<FunctionType>, typename FunctionWrapper<FunctionType>::ResultType (A1, A2, A3, A4, A5, A6)>(FunctionWrapper<FunctionType>(function), a1, a2, a3, a4, a5, a6))); +} + +} + +using WTF::Function; +using WTF::bind; + +#endif // WTF_Functional_h diff --git a/Source/JavaScriptCore/wtf/GetPtr.h b/Source/JavaScriptCore/wtf/GetPtr.h new file mode 100644 index 000000000..25a0e6d9b --- /dev/null +++ b/Source/JavaScriptCore/wtf/GetPtr.h @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2006 Apple Computer, Inc. + * + * This library 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 library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#ifndef WTF_GetPtr_h +#define WTF_GetPtr_h + +namespace WTF { + + template <typename T> inline T* getPtr(T* p) + { + return p; + } + +} // namespace WTF + +#endif // WTF_GetPtr_h diff --git a/Source/JavaScriptCore/wtf/HashCountedSet.h b/Source/JavaScriptCore/wtf/HashCountedSet.h new file mode 100644 index 000000000..b97d8c8fd --- /dev/null +++ b/Source/JavaScriptCore/wtf/HashCountedSet.h @@ -0,0 +1,228 @@ +/* + * Copyright (C) 2005, 2006, 2008 Apple Inc. All rights reserved. + * + * This library 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 library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#ifndef WTF_HashCountedSet_h +#define WTF_HashCountedSet_h + +#include "Assertions.h" +#include "HashMap.h" +#include "Vector.h" + +namespace WTF { + + template<typename Value, typename HashFunctions = typename DefaultHash<Value>::Hash, + typename Traits = HashTraits<Value> > class HashCountedSet { + WTF_MAKE_FAST_ALLOCATED; + private: + typedef HashMap<Value, unsigned, HashFunctions, Traits> ImplType; + public: + typedef Value ValueType; + typedef typename ImplType::iterator iterator; + typedef typename ImplType::const_iterator const_iterator; + + HashCountedSet() {} + + int size() const; + int capacity() const; + bool isEmpty() const; + + // Iterators iterate over pairs of values and counts. + iterator begin(); + iterator end(); + const_iterator begin() const; + const_iterator end() const; + + iterator find(const ValueType&); + const_iterator find(const ValueType&) const; + bool contains(const ValueType&) const; + unsigned count(const ValueType&) const; + + // Increases the count if an equal value is already present + // the return value is a pair of an interator to the new value's + // location, and a bool that is true if an new entry was added. + std::pair<iterator, bool> add(const ValueType&); + + // Reduces the count of the value, and removes it if count + // goes down to zero, returns true if the value is removed. + bool remove(const ValueType&); + bool remove(iterator); + + // Removes the value, regardless of its count. + void removeAll(iterator); + void removeAll(const ValueType&); + + // Clears the whole set. + void clear(); + + private: + ImplType m_impl; + }; + + template<typename Value, typename HashFunctions, typename Traits> + inline int HashCountedSet<Value, HashFunctions, Traits>::size() const + { + return m_impl.size(); + } + + template<typename Value, typename HashFunctions, typename Traits> + inline int HashCountedSet<Value, HashFunctions, Traits>::capacity() const + { + return m_impl.capacity(); + } + + template<typename Value, typename HashFunctions, typename Traits> + inline bool HashCountedSet<Value, HashFunctions, Traits>::isEmpty() const + { + return size() == 0; + } + + template<typename Value, typename HashFunctions, typename Traits> + inline typename HashCountedSet<Value, HashFunctions, Traits>::iterator HashCountedSet<Value, HashFunctions, Traits>::begin() + { + return m_impl.begin(); + } + + template<typename Value, typename HashFunctions, typename Traits> + inline typename HashCountedSet<Value, HashFunctions, Traits>::iterator HashCountedSet<Value, HashFunctions, Traits>::end() + { + return m_impl.end(); + } + + template<typename Value, typename HashFunctions, typename Traits> + inline typename HashCountedSet<Value, HashFunctions, Traits>::const_iterator HashCountedSet<Value, HashFunctions, Traits>::begin() const + { + return m_impl.begin(); + } + + template<typename Value, typename HashFunctions, typename Traits> + inline typename HashCountedSet<Value, HashFunctions, Traits>::const_iterator HashCountedSet<Value, HashFunctions, Traits>::end() const + { + return m_impl.end(); + } + + template<typename Value, typename HashFunctions, typename Traits> + inline typename HashCountedSet<Value, HashFunctions, Traits>::iterator HashCountedSet<Value, HashFunctions, Traits>::find(const ValueType& value) + { + return m_impl.find(value); + } + + template<typename Value, typename HashFunctions, typename Traits> + inline typename HashCountedSet<Value, HashFunctions, Traits>::const_iterator HashCountedSet<Value, HashFunctions, Traits>::find(const ValueType& value) const + { + return m_impl.find(value); + } + + template<typename Value, typename HashFunctions, typename Traits> + inline bool HashCountedSet<Value, HashFunctions, Traits>::contains(const ValueType& value) const + { + return m_impl.contains(value); + } + + template<typename Value, typename HashFunctions, typename Traits> + inline unsigned HashCountedSet<Value, HashFunctions, Traits>::count(const ValueType& value) const + { + return m_impl.get(value); + } + + template<typename Value, typename HashFunctions, typename Traits> + inline std::pair<typename HashCountedSet<Value, HashFunctions, Traits>::iterator, bool> HashCountedSet<Value, HashFunctions, Traits>::add(const ValueType &value) + { + pair<iterator, bool> result = m_impl.add(value, 0); + ++result.first->second; + return result; + } + + template<typename Value, typename HashFunctions, typename Traits> + inline bool HashCountedSet<Value, HashFunctions, Traits>::remove(const ValueType& value) + { + return remove(find(value)); + } + + template<typename Value, typename HashFunctions, typename Traits> + inline bool HashCountedSet<Value, HashFunctions, Traits>::remove(iterator it) + { + if (it == end()) + return false; + + unsigned oldVal = it->second; + ASSERT(oldVal); + unsigned newVal = oldVal - 1; + if (newVal) { + it->second = newVal; + return false; + } + + m_impl.remove(it); + return true; + } + + template<typename Value, typename HashFunctions, typename Traits> + inline void HashCountedSet<Value, HashFunctions, Traits>::removeAll(const ValueType& value) + { + removeAll(find(value)); + } + + template<typename Value, typename HashFunctions, typename Traits> + inline void HashCountedSet<Value, HashFunctions, Traits>::removeAll(iterator it) + { + if (it == end()) + return; + + m_impl.remove(it); + } + + template<typename Value, typename HashFunctions, typename Traits> + inline void HashCountedSet<Value, HashFunctions, Traits>::clear() + { + m_impl.clear(); + } + + template<typename Value, typename HashFunctions, typename Traits, typename VectorType> + inline void copyToVector(const HashCountedSet<Value, HashFunctions, Traits>& collection, VectorType& vector) + { + typedef typename HashCountedSet<Value, HashFunctions, Traits>::const_iterator iterator; + + vector.resize(collection.size()); + + iterator it = collection.begin(); + iterator end = collection.end(); + for (unsigned i = 0; it != end; ++it, ++i) + vector[i] = *it; + } + + template<typename Value, typename HashFunctions, typename Traits> + inline void copyToVector(const HashCountedSet<Value, HashFunctions, Traits>& collection, Vector<Value>& vector) + { + typedef typename HashCountedSet<Value, HashFunctions, Traits>::const_iterator iterator; + + vector.resize(collection.size()); + + iterator it = collection.begin(); + iterator end = collection.end(); + for (unsigned i = 0; it != end; ++it, ++i) + vector[i] = (*it).first; + } + + +} // namespace khtml + +using WTF::HashCountedSet; + +#endif /* WTF_HashCountedSet_h */ diff --git a/Source/JavaScriptCore/wtf/HashFunctions.h b/Source/JavaScriptCore/wtf/HashFunctions.h new file mode 100644 index 000000000..2c66a2d9f --- /dev/null +++ b/Source/JavaScriptCore/wtf/HashFunctions.h @@ -0,0 +1,183 @@ +/* + * Copyright (C) 2005, 2006, 2008 Apple Inc. All rights reserved. + * + * This library 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 library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#ifndef WTF_HashFunctions_h +#define WTF_HashFunctions_h + +#include "RefPtr.h" +#include <stdint.h> + +namespace WTF { + + template<size_t size> struct IntTypes; + template<> struct IntTypes<1> { typedef int8_t SignedType; typedef uint8_t UnsignedType; }; + template<> struct IntTypes<2> { typedef int16_t SignedType; typedef uint16_t UnsignedType; }; + template<> struct IntTypes<4> { typedef int32_t SignedType; typedef uint32_t UnsignedType; }; + template<> struct IntTypes<8> { typedef int64_t SignedType; typedef uint64_t UnsignedType; }; + + // integer hash function + + // Thomas Wang's 32 Bit Mix Function: http://www.cris.com/~Ttwang/tech/inthash.htm + inline unsigned intHash(uint8_t key8) + { + unsigned key = key8; + key += ~(key << 15); + key ^= (key >> 10); + key += (key << 3); + key ^= (key >> 6); + key += ~(key << 11); + key ^= (key >> 16); + return key; + } + + // Thomas Wang's 32 Bit Mix Function: http://www.cris.com/~Ttwang/tech/inthash.htm + inline unsigned intHash(uint16_t key16) + { + unsigned key = key16; + key += ~(key << 15); + key ^= (key >> 10); + key += (key << 3); + key ^= (key >> 6); + key += ~(key << 11); + key ^= (key >> 16); + return key; + } + + // Thomas Wang's 32 Bit Mix Function: http://www.cris.com/~Ttwang/tech/inthash.htm + inline unsigned intHash(uint32_t key) + { + key += ~(key << 15); + key ^= (key >> 10); + key += (key << 3); + key ^= (key >> 6); + key += ~(key << 11); + key ^= (key >> 16); + return key; + } + + // Thomas Wang's 64 bit Mix Function: http://www.cris.com/~Ttwang/tech/inthash.htm + inline unsigned intHash(uint64_t key) + { + key += ~(key << 32); + key ^= (key >> 22); + key += ~(key << 13); + key ^= (key >> 8); + key += (key << 3); + key ^= (key >> 15); + key += ~(key << 27); + key ^= (key >> 31); + return static_cast<unsigned>(key); + } + + template<typename T> struct IntHash { + static unsigned hash(T key) { return intHash(static_cast<typename IntTypes<sizeof(T)>::UnsignedType>(key)); } + static bool equal(T a, T b) { return a == b; } + static const bool safeToCompareToEmptyOrDeleted = true; + }; + + template<typename T> struct FloatHash { + static unsigned hash(T key) + { + union { + T key; + typename IntTypes<sizeof(T)>::UnsignedType bits; + } u; + u.key = key; + return intHash(u.bits); + } + static bool equal(T a, T b) { return a == b; } + static const bool safeToCompareToEmptyOrDeleted = true; + }; + + // pointer identity hash function + + template<typename T> struct PtrHash { + static unsigned hash(T key) + { +#if COMPILER(MSVC) +#pragma warning(push) +#pragma warning(disable: 4244) // work around what seems to be a bug in MSVC's conversion warnings +#endif + return IntHash<uintptr_t>::hash(reinterpret_cast<uintptr_t>(key)); +#if COMPILER(MSVC) +#pragma warning(pop) +#endif + } + static bool equal(T a, T b) { return a == b; } + static const bool safeToCompareToEmptyOrDeleted = true; + }; + template<typename P> struct PtrHash<RefPtr<P> > : PtrHash<P*> { + using PtrHash<P*>::hash; + static unsigned hash(const RefPtr<P>& key) { return hash(key.get()); } + using PtrHash<P*>::equal; + static bool equal(const RefPtr<P>& a, const RefPtr<P>& b) { return a == b; } + static bool equal(P* a, const RefPtr<P>& b) { return a == b; } + static bool equal(const RefPtr<P>& a, P* b) { return a == b; } + }; + + // default hash function for each type + + template<typename T> struct DefaultHash; + + template<typename T, typename U> struct PairHash { + static unsigned hash(const std::pair<T, U>& p) + { + return intHash((static_cast<uint64_t>(DefaultHash<T>::Hash::hash(p.first)) << 32 | DefaultHash<U>::Hash::hash(p.second))); + } + static bool equal(const std::pair<T, U>& a, const std::pair<T, U>& b) + { + return DefaultHash<T>::Hash::equal(a.first, b.first) && DefaultHash<U>::Hash::equal(a.second, b.second); + } + static const bool safeToCompareToEmptyOrDeleted = DefaultHash<T>::Hash::safeToCompareToEmptyOrDeleted + && DefaultHash<U>::Hash::safeToCompareToEmptyOrDeleted; + }; + + // make IntHash the default hash function for many integer types + + template<> struct DefaultHash<short> { typedef IntHash<unsigned> Hash; }; + template<> struct DefaultHash<unsigned short> { typedef IntHash<unsigned> Hash; }; + template<> struct DefaultHash<int> { typedef IntHash<unsigned> Hash; }; + template<> struct DefaultHash<unsigned> { typedef IntHash<unsigned> Hash; }; + template<> struct DefaultHash<long> { typedef IntHash<unsigned long> Hash; }; + template<> struct DefaultHash<unsigned long> { typedef IntHash<unsigned long> Hash; }; + template<> struct DefaultHash<long long> { typedef IntHash<unsigned long long> Hash; }; + template<> struct DefaultHash<unsigned long long> { typedef IntHash<unsigned long long> Hash; }; + +#if !COMPILER(MSVC) || defined(_NATIVE_WCHAR_T_DEFINED) + template<> struct DefaultHash<wchar_t> { typedef IntHash<wchar_t> Hash; }; +#endif + + template<> struct DefaultHash<float> { typedef FloatHash<float> Hash; }; + template<> struct DefaultHash<double> { typedef FloatHash<double> Hash; }; + + // make PtrHash the default hash function for pointer types that don't specialize + + template<typename P> struct DefaultHash<P*> { typedef PtrHash<P*> Hash; }; + template<typename P> struct DefaultHash<RefPtr<P> > { typedef PtrHash<RefPtr<P> > Hash; }; + + template<typename T, typename U> struct DefaultHash<std::pair<T, U> > { typedef PairHash<T, U> Hash; }; + +} // namespace WTF + +using WTF::DefaultHash; +using WTF::IntHash; +using WTF::PtrHash; + +#endif // WTF_HashFunctions_h diff --git a/Source/JavaScriptCore/wtf/HashIterators.h b/Source/JavaScriptCore/wtf/HashIterators.h new file mode 100644 index 000000000..6afa2fa57 --- /dev/null +++ b/Source/JavaScriptCore/wtf/HashIterators.h @@ -0,0 +1,218 @@ +/* + * Copyright (C) 2007 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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 APPLE COMPUTER, INC. ``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 APPLE COMPUTER, INC. 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. + */ + +#ifndef WTF_HashIterators_h +#define WTF_HashIterators_h + +namespace WTF { + + template<typename HashTableType, typename KeyType, typename MappedType> struct HashTableConstKeysIterator; + template<typename HashTableType, typename KeyType, typename MappedType> struct HashTableConstValuesIterator; + template<typename HashTableType, typename KeyType, typename MappedType> struct HashTableKeysIterator; + template<typename HashTableType, typename KeyType, typename MappedType> struct HashTableValuesIterator; + + template<typename HashTableType, typename KeyType, typename MappedType> struct HashTableConstIteratorAdapter<HashTableType, std::pair<KeyType, MappedType> > { + private: + typedef std::pair<KeyType, MappedType> ValueType; + public: + typedef HashTableConstKeysIterator<HashTableType, KeyType, MappedType> Keys; + typedef HashTableConstValuesIterator<HashTableType, KeyType, MappedType> Values; + + HashTableConstIteratorAdapter() {} + HashTableConstIteratorAdapter(const typename HashTableType::const_iterator& impl) : m_impl(impl) {} + + const ValueType* get() const { return (const ValueType*)m_impl.get(); } + const ValueType& operator*() const { return *get(); } + const ValueType* operator->() const { return get(); } + + HashTableConstIteratorAdapter& operator++() { ++m_impl; return *this; } + // postfix ++ intentionally omitted + + Keys keys() { return Keys(*this); } + Values values() { return Values(*this); } + + typename HashTableType::const_iterator m_impl; + }; + + template<typename HashTableType, typename KeyType, typename MappedType> struct HashTableIteratorAdapter<HashTableType, std::pair<KeyType, MappedType> > { + private: + typedef std::pair<KeyType, MappedType> ValueType; + public: + typedef HashTableKeysIterator<HashTableType, KeyType, MappedType> Keys; + typedef HashTableValuesIterator<HashTableType, KeyType, MappedType> Values; + + HashTableIteratorAdapter() {} + HashTableIteratorAdapter(const typename HashTableType::iterator& impl) : m_impl(impl) {} + + ValueType* get() const { return (ValueType*)m_impl.get(); } + ValueType& operator*() const { return *get(); } + ValueType* operator->() const { return get(); } + + HashTableIteratorAdapter& operator++() { ++m_impl; return *this; } + // postfix ++ intentionally omitted + + operator HashTableConstIteratorAdapter<HashTableType, ValueType>() { + typename HashTableType::const_iterator i = m_impl; + return i; + } + + Keys keys() { return Keys(*this); } + Values values() { return Values(*this); } + + typename HashTableType::iterator m_impl; + }; + + template<typename HashTableType, typename KeyType, typename MappedType> struct HashTableConstKeysIterator { + private: + typedef HashTableConstIteratorAdapter<HashTableType, std::pair<KeyType, MappedType> > ConstIterator; + + public: + HashTableConstKeysIterator(const ConstIterator& impl) : m_impl(impl) {} + + const KeyType* get() const { return &(m_impl.get()->first); } + const KeyType& operator*() const { return *get(); } + const KeyType* operator->() const { return get(); } + + HashTableConstKeysIterator& operator++() { ++m_impl; return *this; } + // postfix ++ intentionally omitted + + ConstIterator m_impl; + }; + + template<typename HashTableType, typename KeyType, typename MappedType> struct HashTableConstValuesIterator { + private: + typedef HashTableConstIteratorAdapter<HashTableType, std::pair<KeyType, MappedType> > ConstIterator; + + public: + HashTableConstValuesIterator(const ConstIterator& impl) : m_impl(impl) {} + + const MappedType* get() const { return &(m_impl.get()->second); } + const MappedType& operator*() const { return *get(); } + const MappedType* operator->() const { return get(); } + + HashTableConstValuesIterator& operator++() { ++m_impl; return *this; } + // postfix ++ intentionally omitted + + ConstIterator m_impl; + }; + + template<typename HashTableType, typename KeyType, typename MappedType> struct HashTableKeysIterator { + private: + typedef HashTableIteratorAdapter<HashTableType, std::pair<KeyType, MappedType> > Iterator; + typedef HashTableConstIteratorAdapter<HashTableType, std::pair<KeyType, MappedType> > ConstIterator; + + public: + HashTableKeysIterator(const Iterator& impl) : m_impl(impl) {} + + KeyType* get() const { return &(m_impl.get()->first); } + KeyType& operator*() const { return *get(); } + KeyType* operator->() const { return get(); } + + HashTableKeysIterator& operator++() { ++m_impl; return *this; } + // postfix ++ intentionally omitted + + operator HashTableConstKeysIterator<HashTableType, KeyType, MappedType>() { + ConstIterator i = m_impl; + return i; + } + + Iterator m_impl; + }; + + template<typename HashTableType, typename KeyType, typename MappedType> struct HashTableValuesIterator { + private: + typedef HashTableIteratorAdapter<HashTableType, std::pair<KeyType, MappedType> > Iterator; + typedef HashTableConstIteratorAdapter<HashTableType, std::pair<KeyType, MappedType> > ConstIterator; + + public: + HashTableValuesIterator(const Iterator& impl) : m_impl(impl) {} + + MappedType* get() const { return &(m_impl.get()->second); } + MappedType& operator*() const { return *get(); } + MappedType* operator->() const { return get(); } + + HashTableValuesIterator& operator++() { ++m_impl; return *this; } + // postfix ++ intentionally omitted + + operator HashTableConstValuesIterator<HashTableType, KeyType, MappedType>() { + ConstIterator i = m_impl; + return i; + } + + Iterator m_impl; + }; + + template<typename T, typename U, typename V> + inline bool operator==(const HashTableConstKeysIterator<T, U, V>& a, const HashTableConstKeysIterator<T, U, V>& b) + { + return a.m_impl == b.m_impl; + } + + template<typename T, typename U, typename V> + inline bool operator!=(const HashTableConstKeysIterator<T, U, V>& a, const HashTableConstKeysIterator<T, U, V>& b) + { + return a.m_impl != b.m_impl; + } + + template<typename T, typename U, typename V> + inline bool operator==(const HashTableConstValuesIterator<T, U, V>& a, const HashTableConstValuesIterator<T, U, V>& b) + { + return a.m_impl == b.m_impl; + } + + template<typename T, typename U, typename V> + inline bool operator!=(const HashTableConstValuesIterator<T, U, V>& a, const HashTableConstValuesIterator<T, U, V>& b) + { + return a.m_impl != b.m_impl; + } + + template<typename T, typename U, typename V> + inline bool operator==(const HashTableKeysIterator<T, U, V>& a, const HashTableKeysIterator<T, U, V>& b) + { + return a.m_impl == b.m_impl; + } + + template<typename T, typename U, typename V> + inline bool operator!=(const HashTableKeysIterator<T, U, V>& a, const HashTableKeysIterator<T, U, V>& b) + { + return a.m_impl != b.m_impl; + } + + template<typename T, typename U, typename V> + inline bool operator==(const HashTableValuesIterator<T, U, V>& a, const HashTableValuesIterator<T, U, V>& b) + { + return a.m_impl == b.m_impl; + } + + template<typename T, typename U, typename V> + inline bool operator!=(const HashTableValuesIterator<T, U, V>& a, const HashTableValuesIterator<T, U, V>& b) + { + return a.m_impl != b.m_impl; + } + + +} // namespace WTF + +#endif // WTF_HashIterators_h diff --git a/Source/JavaScriptCore/wtf/HashMap.h b/Source/JavaScriptCore/wtf/HashMap.h new file mode 100644 index 000000000..13e6d496d --- /dev/null +++ b/Source/JavaScriptCore/wtf/HashMap.h @@ -0,0 +1,491 @@ +/* + * Copyright (C) 2005, 2006, 2007, 2008, 2011 Apple Inc. All rights reserved. + * + * This library 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 library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#ifndef WTF_HashMap_h +#define WTF_HashMap_h + +#include "HashTable.h" + +namespace WTF { + + template<typename PairType> struct PairFirstExtractor; + + template<typename T> struct ReferenceTypeMaker { + typedef T& ReferenceType; + }; + template<typename T> struct ReferenceTypeMaker<T&> { + typedef T& ReferenceType; + }; + + template<typename KeyArg, typename MappedArg, typename HashArg = typename DefaultHash<KeyArg>::Hash, + typename KeyTraitsArg = HashTraits<KeyArg>, typename MappedTraitsArg = HashTraits<MappedArg> > + class HashMap { + WTF_MAKE_FAST_ALLOCATED; + private: + typedef KeyTraitsArg KeyTraits; + typedef MappedTraitsArg MappedTraits; + typedef PairHashTraits<KeyTraits, MappedTraits> ValueTraits; + + public: + typedef typename KeyTraits::TraitType KeyType; + typedef typename MappedTraits::TraitType MappedType; + typedef typename ValueTraits::TraitType ValueType; + + private: + typedef typename MappedTraits::PassInType MappedPassInType; + typedef typename MappedTraits::PassOutType MappedPassOutType; + typedef typename MappedTraits::PeekType MappedPeekType; + + typedef typename ReferenceTypeMaker<MappedPassInType>::ReferenceType MappedPassInReferenceType; + + typedef HashArg HashFunctions; + + typedef HashTable<KeyType, ValueType, PairFirstExtractor<ValueType>, + HashFunctions, ValueTraits, KeyTraits> HashTableType; + + class HashMapKeysProxy; + class HashMapValuesProxy; + + public: + typedef HashTableIteratorAdapter<HashTableType, ValueType> iterator; + typedef HashTableConstIteratorAdapter<HashTableType, ValueType> const_iterator; + + public: + void swap(HashMap&); + + int size() const; + int capacity() const; + bool isEmpty() const; + + // iterators iterate over pairs of keys and values + iterator begin(); + iterator end(); + const_iterator begin() const; + const_iterator end() const; + + HashMapKeysProxy& keys() { return static_cast<HashMapKeysProxy&>(*this); } + const HashMapKeysProxy& keys() const { return static_cast<const HashMapKeysProxy&>(*this); } + + HashMapValuesProxy& values() { return static_cast<HashMapValuesProxy&>(*this); } + const HashMapValuesProxy& values() const { return static_cast<const HashMapValuesProxy&>(*this); } + + iterator find(const KeyType&); + const_iterator find(const KeyType&) const; + bool contains(const KeyType&) const; + MappedPeekType get(const KeyType&) const; + + // replaces value but not key if key is already present + // return value is a pair of the iterator to the key location, + // and a boolean that's true if a new value was actually added + pair<iterator, bool> set(const KeyType&, MappedPassInType); + + // does nothing if key is already present + // return value is a pair of the iterator to the key location, + // and a boolean that's true if a new value was actually added + pair<iterator, bool> add(const KeyType&, MappedPassInType); + + void remove(const KeyType&); + void remove(iterator); + void clear(); + + MappedPassOutType take(const KeyType&); // efficient combination of get with remove + + // An alternate version of find() that finds the object by hashing and comparing + // with some other type, to avoid the cost of type conversion. HashTranslator + // must have the following function members: + // static unsigned hash(const T&); + // static bool equal(const ValueType&, const T&); + template<typename T, typename HashTranslator> iterator find(const T&); + template<typename T, typename HashTranslator> const_iterator find(const T&) const; + template<typename T, typename HashTranslator> bool contains(const T&) const; + + // An alternate version of add() that finds the object by hashing and comparing + // with some other type, to avoid the cost of type conversion if the object is already + // in the table. HashTranslator must have the following function members: + // static unsigned hash(const T&); + // static bool equal(const ValueType&, const T&); + // static translate(ValueType&, const T&, unsigned hashCode); + template<typename T, typename HashTranslator> pair<iterator, bool> add(const T&, MappedPassInType); + + void checkConsistency() const; + + private: + pair<iterator, bool> inlineAdd(const KeyType&, MappedPassInReferenceType); + + class HashMapKeysProxy : private HashMap { + public: + typedef typename HashMap::iterator::Keys iterator; + typedef typename HashMap::const_iterator::Keys const_iterator; + + iterator begin() + { + return HashMap::begin().keys(); + } + + iterator end() + { + return HashMap::end().keys(); + } + + const_iterator begin() const + { + return HashMap::begin().keys(); + } + + const_iterator end() const + { + return HashMap::end().keys(); + } + + private: + friend class HashMap; + + // These are intentionally not implemented. + HashMapKeysProxy(); + HashMapKeysProxy(const HashMapKeysProxy&); + HashMapKeysProxy& operator=(const HashMapKeysProxy&); + ~HashMapKeysProxy(); + }; + + class HashMapValuesProxy : private HashMap { + public: + typedef typename HashMap::iterator::Values iterator; + typedef typename HashMap::const_iterator::Values const_iterator; + + iterator begin() + { + return HashMap::begin().values(); + } + + iterator end() + { + return HashMap::end().values(); + } + + const_iterator begin() const + { + return HashMap::begin().values(); + } + + const_iterator end() const + { + return HashMap::end().values(); + } + + private: + friend class HashMap; + + // These are intentionally not implemented. + HashMapValuesProxy(); + HashMapValuesProxy(const HashMapValuesProxy&); + HashMapValuesProxy& operator=(const HashMapValuesProxy&); + ~HashMapValuesProxy(); + }; + + HashTableType m_impl; + }; + + template<typename PairType> struct PairFirstExtractor { + static const typename PairType::first_type& extract(const PairType& p) { return p.first; } + }; + + template<typename ValueTraits, typename HashFunctions> + struct HashMapTranslator { + template<typename T> static unsigned hash(const T& key) { return HashFunctions::hash(key); } + template<typename T, typename U> static bool equal(const T& a, const U& b) { return HashFunctions::equal(a, b); } + template<typename T, typename U, typename V> static void translate(T& location, const U& key, const V& mapped) + { + location.first = key; + ValueTraits::SecondTraits::store(mapped, location.second); + } + }; + + template<typename ValueTraits, typename Translator> + struct HashMapTranslatorAdapter { + template<typename T> static unsigned hash(const T& key) { return Translator::hash(key); } + template<typename T, typename U> static bool equal(const T& a, const U& b) { return Translator::equal(a, b); } + template<typename T, typename U, typename V> static void translate(T& location, const U& key, const V& mapped, unsigned hashCode) + { + Translator::translate(location.first, key, hashCode); + ValueTraits::SecondTraits::store(mapped, location.second); + } + }; + + template<typename T, typename U, typename V, typename W, typename X> + inline void HashMap<T, U, V, W, X>::swap(HashMap& other) + { + m_impl.swap(other.m_impl); + } + + template<typename T, typename U, typename V, typename W, typename X> + inline int HashMap<T, U, V, W, X>::size() const + { + return m_impl.size(); + } + + template<typename T, typename U, typename V, typename W, typename X> + inline int HashMap<T, U, V, W, X>::capacity() const + { + return m_impl.capacity(); + } + + template<typename T, typename U, typename V, typename W, typename X> + inline bool HashMap<T, U, V, W, X>::isEmpty() const + { + return m_impl.isEmpty(); + } + + template<typename T, typename U, typename V, typename W, typename X> + inline typename HashMap<T, U, V, W, X>::iterator HashMap<T, U, V, W, X>::begin() + { + return m_impl.begin(); + } + + template<typename T, typename U, typename V, typename W, typename X> + inline typename HashMap<T, U, V, W, X>::iterator HashMap<T, U, V, W, X>::end() + { + return m_impl.end(); + } + + template<typename T, typename U, typename V, typename W, typename X> + inline typename HashMap<T, U, V, W, X>::const_iterator HashMap<T, U, V, W, X>::begin() const + { + return m_impl.begin(); + } + + template<typename T, typename U, typename V, typename W, typename X> + inline typename HashMap<T, U, V, W, X>::const_iterator HashMap<T, U, V, W, X>::end() const + { + return m_impl.end(); + } + + template<typename T, typename U, typename V, typename W, typename X> + inline typename HashMap<T, U, V, W, X>::iterator HashMap<T, U, V, W, X>::find(const KeyType& key) + { + return m_impl.find(key); + } + + template<typename T, typename U, typename V, typename W, typename X> + inline typename HashMap<T, U, V, W, X>::const_iterator HashMap<T, U, V, W, X>::find(const KeyType& key) const + { + return m_impl.find(key); + } + + template<typename T, typename U, typename V, typename W, typename X> + inline bool HashMap<T, U, V, W, X>::contains(const KeyType& key) const + { + return m_impl.contains(key); + } + + template<typename T, typename U, typename V, typename W, typename X> + template<typename TYPE, typename HashTranslator> + inline typename HashMap<T, U, V, W, X>::iterator + HashMap<T, U, V, W, X>::find(const TYPE& value) + { + return m_impl.template find<HashMapTranslatorAdapter<ValueTraits, HashTranslator> >(value); + } + + template<typename T, typename U, typename V, typename W, typename X> + template<typename TYPE, typename HashTranslator> + inline typename HashMap<T, U, V, W, X>::const_iterator + HashMap<T, U, V, W, X>::find(const TYPE& value) const + { + return m_impl.template find<HashMapTranslatorAdapter<ValueTraits, HashTranslator> >(value); + } + + template<typename T, typename U, typename V, typename W, typename X> + template<typename TYPE, typename HashTranslator> + inline bool + HashMap<T, U, V, W, X>::contains(const TYPE& value) const + { + return m_impl.template contains<HashMapTranslatorAdapter<ValueTraits, HashTranslator> >(value); + } + + template<typename T, typename U, typename V, typename W, typename X> + inline pair<typename HashMap<T, U, V, W, X>::iterator, bool> + HashMap<T, U, V, W, X>::inlineAdd(const KeyType& key, MappedPassInReferenceType mapped) + { + return m_impl.template add<HashMapTranslator<ValueTraits, HashFunctions> >(key, mapped); + } + + template<typename T, typename U, typename V, typename W, typename X> + pair<typename HashMap<T, U, V, W, X>::iterator, bool> + HashMap<T, U, V, W, X>::set(const KeyType& key, MappedPassInType mapped) + { + pair<iterator, bool> result = inlineAdd(key, mapped); + if (!result.second) { + // The inlineAdd call above found an existing hash table entry; we need to set the mapped value. + MappedTraits::store(mapped, result.first->second); + } + return result; + } + + template<typename T, typename U, typename V, typename W, typename X> + template<typename TYPE, typename HashTranslator> + pair<typename HashMap<T, U, V, W, X>::iterator, bool> + HashMap<T, U, V, W, X>::add(const TYPE& key, MappedPassInType value) + { + return m_impl.template addPassingHashCode<HashMapTranslatorAdapter<ValueTraits, HashTranslator> >(key, value); + } + + template<typename T, typename U, typename V, typename W, typename X> + pair<typename HashMap<T, U, V, W, X>::iterator, bool> + HashMap<T, U, V, W, X>::add(const KeyType& key, MappedPassInType mapped) + { + return inlineAdd(key, mapped); + } + + template<typename T, typename U, typename V, typename W, typename MappedTraits> + typename HashMap<T, U, V, W, MappedTraits>::MappedPeekType + HashMap<T, U, V, W, MappedTraits>::get(const KeyType& key) const + { + ValueType* entry = const_cast<HashTableType&>(m_impl).lookup(key); + if (!entry) + return MappedTraits::peek(MappedTraits::emptyValue()); + return MappedTraits::peek(entry->second); + } + + template<typename T, typename U, typename V, typename W, typename X> + inline void HashMap<T, U, V, W, X>::remove(iterator it) + { + if (it.m_impl == m_impl.end()) + return; + m_impl.internalCheckTableConsistency(); + m_impl.removeWithoutEntryConsistencyCheck(it.m_impl); + } + + template<typename T, typename U, typename V, typename W, typename X> + inline void HashMap<T, U, V, W, X>::remove(const KeyType& key) + { + remove(find(key)); + } + + template<typename T, typename U, typename V, typename W, typename X> + inline void HashMap<T, U, V, W, X>::clear() + { + m_impl.clear(); + } + + template<typename T, typename U, typename V, typename W, typename MappedTraits> + typename HashMap<T, U, V, W, MappedTraits>::MappedPassOutType + HashMap<T, U, V, W, MappedTraits>::take(const KeyType& key) + { + iterator it = find(key); + if (it == end()) + return MappedTraits::passOut(MappedTraits::emptyValue()); + MappedPassOutType result = MappedTraits::passOut(it->second); + remove(it); + return result; + } + + template<typename T, typename U, typename V, typename W, typename X> + inline void HashMap<T, U, V, W, X>::checkConsistency() const + { + m_impl.checkTableConsistency(); + } + + template<typename T, typename U, typename V, typename W, typename X> + bool operator==(const HashMap<T, U, V, W, X>& a, const HashMap<T, U, V, W, X>& b) + { + if (a.size() != b.size()) + return false; + + typedef typename HashMap<T, U, V, W, X>::const_iterator const_iterator; + + const_iterator end = a.end(); + const_iterator notFound = b.end(); + for (const_iterator it = a.begin(); it != end; ++it) { + const_iterator bPos = b.find(it->first); + if (bPos == notFound || it->second != bPos->second) + return false; + } + + return true; + } + + template<typename T, typename U, typename V, typename W, typename X> + inline bool operator!=(const HashMap<T, U, V, W, X>& a, const HashMap<T, U, V, W, X>& b) + { + return !(a == b); + } + + template<typename HashTableType> + void deleteAllPairSeconds(HashTableType& collection) + { + typedef typename HashTableType::const_iterator iterator; + iterator end = collection.end(); + for (iterator it = collection.begin(); it != end; ++it) + delete it->second; + } + + template<typename T, typename U, typename V, typename W, typename X> + inline void deleteAllValues(const HashMap<T, U, V, W, X>& collection) + { + deleteAllPairSeconds(collection); + } + + template<typename HashTableType> + void deleteAllPairFirsts(HashTableType& collection) + { + typedef typename HashTableType::const_iterator iterator; + iterator end = collection.end(); + for (iterator it = collection.begin(); it != end; ++it) + delete it->first; + } + + template<typename T, typename U, typename V, typename W, typename X> + inline void deleteAllKeys(const HashMap<T, U, V, W, X>& collection) + { + deleteAllPairFirsts(collection); + } + + template<typename T, typename U, typename V, typename W, typename X, typename Y> + inline void copyKeysToVector(const HashMap<T, U, V, W, X>& collection, Y& vector) + { + typedef typename HashMap<T, U, V, W, X>::const_iterator::Keys iterator; + + vector.resize(collection.size()); + + iterator it = collection.begin().keys(); + iterator end = collection.end().keys(); + for (unsigned i = 0; it != end; ++it, ++i) + vector[i] = *it; + } + + template<typename T, typename U, typename V, typename W, typename X, typename Y> + inline void copyValuesToVector(const HashMap<T, U, V, W, X>& collection, Y& vector) + { + typedef typename HashMap<T, U, V, W, X>::const_iterator::Values iterator; + + vector.resize(collection.size()); + + iterator it = collection.begin().values(); + iterator end = collection.end().values(); + for (unsigned i = 0; it != end; ++it, ++i) + vector[i] = *it; + } + +} // namespace WTF + +using WTF::HashMap; + +#include "RefPtrHashMap.h" + +#endif /* WTF_HashMap_h */ diff --git a/Source/JavaScriptCore/wtf/HashSet.h b/Source/JavaScriptCore/wtf/HashSet.h new file mode 100644 index 000000000..7fce19348 --- /dev/null +++ b/Source/JavaScriptCore/wtf/HashSet.h @@ -0,0 +1,261 @@ +/* + * Copyright (C) 2005, 2006, 2007, 2008, 2011 Apple Inc. All rights reserved. + * + * This library 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 library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#ifndef WTF_HashSet_h +#define WTF_HashSet_h + +#include "FastAllocBase.h" +#include "HashTable.h" + +namespace WTF { + + struct IdentityExtractor; + + template<typename Value, typename HashFunctions, typename Traits> class HashSet; + template<typename Value, typename HashFunctions, typename Traits> + void deleteAllValues(const HashSet<Value, HashFunctions, Traits>&); + template<typename Value, typename HashFunctions, typename Traits> + void fastDeleteAllValues(const HashSet<Value, HashFunctions, Traits>&); + + template<typename ValueArg, typename HashArg = typename DefaultHash<ValueArg>::Hash, + typename TraitsArg = HashTraits<ValueArg> > class HashSet { + WTF_MAKE_FAST_ALLOCATED; + private: + typedef HashArg HashFunctions; + typedef TraitsArg ValueTraits; + + public: + typedef typename ValueTraits::TraitType ValueType; + + private: + typedef HashTable<ValueType, ValueType, IdentityExtractor, + HashFunctions, ValueTraits, ValueTraits> HashTableType; + + public: + typedef HashTableConstIteratorAdapter<HashTableType, ValueType> iterator; + typedef HashTableConstIteratorAdapter<HashTableType, ValueType> const_iterator; + + void swap(HashSet&); + + int size() const; + int capacity() const; + bool isEmpty() const; + + iterator begin() const; + iterator end() const; + + iterator find(const ValueType&) const; + bool contains(const ValueType&) const; + + // An alternate version of find() that finds the object by hashing and comparing + // with some other type, to avoid the cost of type conversion. HashTranslator + // must have the following function members: + // static unsigned hash(const T&); + // static bool equal(const ValueType&, const T&); + // FIXME: We should reverse the order of the template arguments so that callers + // can just pass the translator and let the compiler deduce T. + template<typename T, typename HashTranslator> iterator find(const T&) const; + template<typename T, typename HashTranslator> bool contains(const T&) const; + + // The return value is a pair of an interator to the new value's location, + // and a bool that is true if an new entry was added. + pair<iterator, bool> add(const ValueType&); + + // An alternate version of add() that finds the object by hashing and comparing + // with some other type, to avoid the cost of type conversion if the object is already + // in the table. HashTranslator must have the following function members: + // static unsigned hash(const T&); + // static bool equal(const ValueType&, const T&); + // static translate(ValueType&, const T&, unsigned hashCode); + // FIXME: We should reverse the order of the template arguments so that callers + // can just pass the translator and let the compiler deduce T. + template<typename T, typename HashTranslator> pair<iterator, bool> add(const T&); + + void remove(const ValueType&); + void remove(iterator); + void clear(); + + private: + friend void deleteAllValues<>(const HashSet&); + friend void fastDeleteAllValues<>(const HashSet&); + + HashTableType m_impl; + }; + + struct IdentityExtractor { + template<typename T> static const T& extract(const T& t) { return t; } + }; + + template<typename Translator> + struct HashSetTranslatorAdapter { + template<typename T> static unsigned hash(const T& key) { return Translator::hash(key); } + template<typename T, typename U> static bool equal(const T& a, const U& b) { return Translator::equal(a, b); } + template<typename T, typename U> static void translate(T& location, const U& key, const U&, unsigned hashCode) + { + Translator::translate(location, key, hashCode); + } + }; + + template<typename T, typename U, typename V> + inline void HashSet<T, U, V>::swap(HashSet& other) + { + m_impl.swap(other.m_impl); + } + + template<typename T, typename U, typename V> + inline int HashSet<T, U, V>::size() const + { + return m_impl.size(); + } + + template<typename T, typename U, typename V> + inline int HashSet<T, U, V>::capacity() const + { + return m_impl.capacity(); + } + + template<typename T, typename U, typename V> + inline bool HashSet<T, U, V>::isEmpty() const + { + return m_impl.isEmpty(); + } + + template<typename T, typename U, typename V> + inline typename HashSet<T, U, V>::iterator HashSet<T, U, V>::begin() const + { + return m_impl.begin(); + } + + template<typename T, typename U, typename V> + inline typename HashSet<T, U, V>::iterator HashSet<T, U, V>::end() const + { + return m_impl.end(); + } + + template<typename T, typename U, typename V> + inline typename HashSet<T, U, V>::iterator HashSet<T, U, V>::find(const ValueType& value) const + { + return m_impl.find(value); + } + + template<typename T, typename U, typename V> + inline bool HashSet<T, U, V>::contains(const ValueType& value) const + { + return m_impl.contains(value); + } + + template<typename Value, typename HashFunctions, typename Traits> + template<typename T, typename HashTranslator> + typename HashSet<Value, HashFunctions, Traits>::iterator + inline HashSet<Value, HashFunctions, Traits>::find(const T& value) const + { + return m_impl.template find<HashSetTranslatorAdapter<HashTranslator> >(value); + } + + template<typename Value, typename HashFunctions, typename Traits> + template<typename T, typename HashTranslator> + inline bool HashSet<Value, HashFunctions, Traits>::contains(const T& value) const + { + return m_impl.template contains<HashSetTranslatorAdapter<HashTranslator> >(value); + } + + template<typename T, typename U, typename V> + inline pair<typename HashSet<T, U, V>::iterator, bool> HashSet<T, U, V>::add(const ValueType& value) + { + return m_impl.add(value); + } + + template<typename Value, typename HashFunctions, typename Traits> + template<typename T, typename HashTranslator> + inline pair<typename HashSet<Value, HashFunctions, Traits>::iterator, bool> + HashSet<Value, HashFunctions, Traits>::add(const T& value) + { + return m_impl.template addPassingHashCode<HashSetTranslatorAdapter<HashTranslator> >(value, value); + } + + template<typename T, typename U, typename V> + inline void HashSet<T, U, V>::remove(iterator it) + { + if (it.m_impl == m_impl.end()) + return; + m_impl.internalCheckTableConsistency(); + m_impl.removeWithoutEntryConsistencyCheck(it.m_impl); + } + + template<typename T, typename U, typename V> + inline void HashSet<T, U, V>::remove(const ValueType& value) + { + remove(find(value)); + } + + template<typename T, typename U, typename V> + inline void HashSet<T, U, V>::clear() + { + m_impl.clear(); + } + + template<typename ValueType, typename HashTableType> + void deleteAllValues(HashTableType& collection) + { + typedef typename HashTableType::const_iterator iterator; + iterator end = collection.end(); + for (iterator it = collection.begin(); it != end; ++it) + delete *it; + } + + template<typename T, typename U, typename V> + inline void deleteAllValues(const HashSet<T, U, V>& collection) + { + deleteAllValues<typename HashSet<T, U, V>::ValueType>(collection.m_impl); + } + + template<typename ValueType, typename HashTableType> + void fastDeleteAllValues(HashTableType& collection) + { + typedef typename HashTableType::const_iterator iterator; + iterator end = collection.end(); + for (iterator it = collection.begin(); it != end; ++it) + fastDelete(*it); + } + + template<typename T, typename U, typename V> + inline void fastDeleteAllValues(const HashSet<T, U, V>& collection) + { + fastDeleteAllValues<typename HashSet<T, U, V>::ValueType>(collection.m_impl); + } + + template<typename T, typename U, typename V, typename W> + inline void copyToVector(const HashSet<T, U, V>& collection, W& vector) + { + typedef typename HashSet<T, U, V>::const_iterator iterator; + + vector.resize(collection.size()); + + iterator it = collection.begin(); + iterator end = collection.end(); + for (unsigned i = 0; it != end; ++it, ++i) + vector[i] = *it; + } + +} // namespace WTF + +using WTF::HashSet; + +#endif /* WTF_HashSet_h */ diff --git a/Source/JavaScriptCore/wtf/HashTable.cpp b/Source/JavaScriptCore/wtf/HashTable.cpp new file mode 100644 index 000000000..71d3f86ce --- /dev/null +++ b/Source/JavaScriptCore/wtf/HashTable.cpp @@ -0,0 +1,69 @@ +/* + Copyright (C) 2005 Apple Inc. All rights reserved. + + This library 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 library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "config.h" +#include "HashTable.h" + +namespace WTF { + +#if DUMP_HASHTABLE_STATS + +int HashTableStats::numAccesses; +int HashTableStats::numCollisions; +int HashTableStats::collisionGraph[4096]; +int HashTableStats::maxCollisions; +int HashTableStats::numRehashes; +int HashTableStats::numRemoves; +int HashTableStats::numReinserts; + +static HashTableStats logger; + +static Mutex& hashTableStatsMutex() +{ + AtomicallyInitializedStatic(Mutex&, mutex = *new Mutex); + return mutex; +} + +HashTableStats::~HashTableStats() +{ + // Don't lock hashTableStatsMutex here because it can cause deadlocks at shutdown + // if any thread was killed while holding the mutex. + printf("\nWTF::HashTable statistics\n\n"); + printf("%d accesses\n", numAccesses); + printf("%d total collisions, average %.2f probes per access\n", numCollisions, 1.0 * (numAccesses + numCollisions) / numAccesses); + printf("longest collision chain: %d\n", maxCollisions); + for (int i = 1; i <= maxCollisions; i++) { + printf(" %d lookups with exactly %d collisions (%.2f%% , %.2f%% with this many or more)\n", collisionGraph[i], i, 100.0 * (collisionGraph[i] - collisionGraph[i+1]) / numAccesses, 100.0 * collisionGraph[i] / numAccesses); + } + printf("%d rehashes\n", numRehashes); + printf("%d reinserts\n", numReinserts); +} + +void HashTableStats::recordCollisionAtCount(int count) +{ + MutexLocker lock(hashTableStatsMutex()); + if (count > maxCollisions) + maxCollisions = count; + numCollisions++; + collisionGraph[count]++; +} + +#endif + +} // namespace WTF diff --git a/Source/JavaScriptCore/wtf/HashTable.h b/Source/JavaScriptCore/wtf/HashTable.h new file mode 100644 index 000000000..44f914330 --- /dev/null +++ b/Source/JavaScriptCore/wtf/HashTable.h @@ -0,0 +1,1244 @@ +/* + * Copyright (C) 2005, 2006, 2007, 2008, 2011 Apple Inc. All rights reserved. + * Copyright (C) 2008 David Levin <levin@chromium.org> + * + * This library 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 library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#ifndef WTF_HashTable_h +#define WTF_HashTable_h + +#include "Alignment.h" +#include "Assertions.h" +#include "FastMalloc.h" +#include "HashTraits.h" +#include "StdLibExtras.h" +#include "Threading.h" +#include "ValueCheck.h" + +namespace WTF { + +#define DUMP_HASHTABLE_STATS 0 + +// Enables internal WTF consistency checks that are invoked automatically. Non-WTF callers can call checkTableConsistency() even if internal checks are disabled. +#define CHECK_HASHTABLE_CONSISTENCY 0 + +#ifdef NDEBUG +#define CHECK_HASHTABLE_ITERATORS 0 +#define CHECK_HASHTABLE_USE_AFTER_DESTRUCTION 0 +#else +#define CHECK_HASHTABLE_ITERATORS 1 +#define CHECK_HASHTABLE_USE_AFTER_DESTRUCTION 1 +#endif + +#if DUMP_HASHTABLE_STATS + + struct HashTableStats { + ~HashTableStats(); + // All of the variables are accessed in ~HashTableStats when the static struct is destroyed. + + // The following variables are all atomically incremented when modified. + static int numAccesses; + static int numRehashes; + static int numRemoves; + static int numReinserts; + + // The following variables are only modified in the recordCollisionAtCount method within a mutex. + static int maxCollisions; + static int numCollisions; + static int collisionGraph[4096]; + + static void recordCollisionAtCount(int count); + }; + +#endif + + template<typename Key, typename Value, typename Extractor, typename HashFunctions, typename Traits, typename KeyTraits> + class HashTable; + template<typename Key, typename Value, typename Extractor, typename HashFunctions, typename Traits, typename KeyTraits> + class HashTableIterator; + template<typename Key, typename Value, typename Extractor, typename HashFunctions, typename Traits, typename KeyTraits> + class HashTableConstIterator; + + template<typename Key, typename Value, typename Extractor, typename HashFunctions, typename Traits, typename KeyTraits> + void addIterator(const HashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits>*, + HashTableConstIterator<Key, Value, Extractor, HashFunctions, Traits, KeyTraits>*); + + template<typename Key, typename Value, typename Extractor, typename HashFunctions, typename Traits, typename KeyTraits> + void removeIterator(HashTableConstIterator<Key, Value, Extractor, HashFunctions, Traits, KeyTraits>*); + +#if !CHECK_HASHTABLE_ITERATORS + + template<typename Key, typename Value, typename Extractor, typename HashFunctions, typename Traits, typename KeyTraits> + inline void addIterator(const HashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits>*, + HashTableConstIterator<Key, Value, Extractor, HashFunctions, Traits, KeyTraits>*) { } + + template<typename Key, typename Value, typename Extractor, typename HashFunctions, typename Traits, typename KeyTraits> + inline void removeIterator(HashTableConstIterator<Key, Value, Extractor, HashFunctions, Traits, KeyTraits>*) { } + +#endif + + typedef enum { HashItemKnownGood } HashItemKnownGoodTag; + + template<typename Key, typename Value, typename Extractor, typename HashFunctions, typename Traits, typename KeyTraits> + class HashTableConstIterator { + private: + typedef HashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits> HashTableType; + typedef HashTableIterator<Key, Value, Extractor, HashFunctions, Traits, KeyTraits> iterator; + typedef HashTableConstIterator<Key, Value, Extractor, HashFunctions, Traits, KeyTraits> const_iterator; + typedef Value ValueType; + typedef const ValueType& ReferenceType; + typedef const ValueType* PointerType; + + friend class HashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits>; + friend class HashTableIterator<Key, Value, Extractor, HashFunctions, Traits, KeyTraits>; + + void skipEmptyBuckets() + { + while (m_position != m_endPosition && HashTableType::isEmptyOrDeletedBucket(*m_position)) + ++m_position; + } + + HashTableConstIterator(const HashTableType* table, PointerType position, PointerType endPosition) + : m_position(position), m_endPosition(endPosition) + { + addIterator(table, this); + skipEmptyBuckets(); + } + + HashTableConstIterator(const HashTableType* table, PointerType position, PointerType endPosition, HashItemKnownGoodTag) + : m_position(position), m_endPosition(endPosition) + { + addIterator(table, this); + } + + public: + HashTableConstIterator() + { + addIterator(static_cast<const HashTableType*>(0), this); + } + + // default copy, assignment and destructor are OK if CHECK_HASHTABLE_ITERATORS is 0 + +#if CHECK_HASHTABLE_ITERATORS + ~HashTableConstIterator() + { + removeIterator(this); + } + + HashTableConstIterator(const const_iterator& other) + : m_position(other.m_position), m_endPosition(other.m_endPosition) + { + addIterator(other.m_table, this); + } + + const_iterator& operator=(const const_iterator& other) + { + m_position = other.m_position; + m_endPosition = other.m_endPosition; + + removeIterator(this); + addIterator(other.m_table, this); + + return *this; + } +#endif + + PointerType get() const + { + checkValidity(); + return m_position; + } + ReferenceType operator*() const { return *get(); } + PointerType operator->() const { return get(); } + + const_iterator& operator++() + { + checkValidity(); + ASSERT(m_position != m_endPosition); + ++m_position; + skipEmptyBuckets(); + return *this; + } + + // postfix ++ intentionally omitted + + // Comparison. + bool operator==(const const_iterator& other) const + { + checkValidity(other); + return m_position == other.m_position; + } + bool operator!=(const const_iterator& other) const + { + checkValidity(other); + return m_position != other.m_position; + } + bool operator==(const iterator& other) const + { + return *this == static_cast<const_iterator>(other); + } + bool operator!=(const iterator& other) const + { + return *this != static_cast<const_iterator>(other); + } + + private: + void checkValidity() const + { +#if CHECK_HASHTABLE_ITERATORS + ASSERT(m_table); +#endif + } + + +#if CHECK_HASHTABLE_ITERATORS + void checkValidity(const const_iterator& other) const + { + ASSERT(m_table); + ASSERT_UNUSED(other, other.m_table); + ASSERT(m_table == other.m_table); + } +#else + void checkValidity(const const_iterator&) const { } +#endif + + PointerType m_position; + PointerType m_endPosition; + +#if CHECK_HASHTABLE_ITERATORS + public: + // Any modifications of the m_next or m_previous of an iterator that is in a linked list of a HashTable::m_iterator, + // should be guarded with m_table->m_mutex. + mutable const HashTableType* m_table; + mutable const_iterator* m_next; + mutable const_iterator* m_previous; +#endif + }; + + template<typename Key, typename Value, typename Extractor, typename HashFunctions, typename Traits, typename KeyTraits> + class HashTableIterator { + private: + typedef HashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits> HashTableType; + typedef HashTableIterator<Key, Value, Extractor, HashFunctions, Traits, KeyTraits> iterator; + typedef HashTableConstIterator<Key, Value, Extractor, HashFunctions, Traits, KeyTraits> const_iterator; + typedef Value ValueType; + typedef ValueType& ReferenceType; + typedef ValueType* PointerType; + + friend class HashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits>; + + HashTableIterator(HashTableType* table, PointerType pos, PointerType end) : m_iterator(table, pos, end) { } + HashTableIterator(HashTableType* table, PointerType pos, PointerType end, HashItemKnownGoodTag tag) : m_iterator(table, pos, end, tag) { } + + public: + HashTableIterator() { } + + // default copy, assignment and destructor are OK + + PointerType get() const { return const_cast<PointerType>(m_iterator.get()); } + ReferenceType operator*() const { return *get(); } + PointerType operator->() const { return get(); } + + iterator& operator++() { ++m_iterator; return *this; } + + // postfix ++ intentionally omitted + + // Comparison. + bool operator==(const iterator& other) const { return m_iterator == other.m_iterator; } + bool operator!=(const iterator& other) const { return m_iterator != other.m_iterator; } + bool operator==(const const_iterator& other) const { return m_iterator == other; } + bool operator!=(const const_iterator& other) const { return m_iterator != other; } + + operator const_iterator() const { return m_iterator; } + + private: + const_iterator m_iterator; + }; + + using std::swap; + + // Work around MSVC's standard library, whose swap for pairs does not swap by component. + template<typename T> inline void hashTableSwap(T& a, T& b) + { + swap(a, b); + } + + // Swap pairs by component, in case of pair members that specialize swap. + template<typename T, typename U> inline void hashTableSwap(pair<T, U>& a, pair<T, U>& b) + { + swap(a.first, b.first); + swap(a.second, b.second); + } + + template<typename T, bool useSwap> struct Mover; + template<typename T> struct Mover<T, true> { static void move(T& from, T& to) { hashTableSwap(from, to); } }; + template<typename T> struct Mover<T, false> { static void move(T& from, T& to) { to = from; } }; + + template<typename HashFunctions> class IdentityHashTranslator { + public: + template<typename T> static unsigned hash(const T& key) { return HashFunctions::hash(key); } + template<typename T> static bool equal(const T& a, const T& b) { return HashFunctions::equal(a, b); } + template<typename T, typename U> static void translate(T& location, const U&, const T& value) { location = value; } + }; + + template<typename Key, typename Value, typename Extractor, typename HashFunctions, typename Traits, typename KeyTraits> + class HashTable { + public: + typedef HashTableIterator<Key, Value, Extractor, HashFunctions, Traits, KeyTraits> iterator; + typedef HashTableConstIterator<Key, Value, Extractor, HashFunctions, Traits, KeyTraits> const_iterator; + typedef Traits ValueTraits; + typedef Key KeyType; + typedef Value ValueType; + typedef IdentityHashTranslator<HashFunctions> IdentityTranslatorType; + + HashTable(); + ~HashTable() + { + invalidateIterators(); + deallocateTable(m_table, m_tableSize); +#if CHECK_HASHTABLE_USE_AFTER_DESTRUCTION + m_table = (ValueType*)(uintptr_t)0xbbadbeef; +#endif + } + + HashTable(const HashTable&); + void swap(HashTable&); + HashTable& operator=(const HashTable&); + + iterator begin() { return makeIterator(m_table); } + iterator end() { return makeKnownGoodIterator(m_table + m_tableSize); } + const_iterator begin() const { return makeConstIterator(m_table); } + const_iterator end() const { return makeKnownGoodConstIterator(m_table + m_tableSize); } + + int size() const { return m_keyCount; } + int capacity() const { return m_tableSize; } + bool isEmpty() const { return !m_keyCount; } + + pair<iterator, bool> add(const ValueType& value) { return add<IdentityTranslatorType>(Extractor::extract(value), value); } + + // A special version of add() that finds the object by hashing and comparing + // with some other type, to avoid the cost of type conversion if the object is already + // in the table. + template<typename HashTranslator, typename T, typename Extra> pair<iterator, bool> add(const T& key, const Extra&); + template<typename HashTranslator, typename T, typename Extra> pair<iterator, bool> addPassingHashCode(const T& key, const Extra&); + + iterator find(const KeyType& key) { return find<IdentityTranslatorType>(key); } + const_iterator find(const KeyType& key) const { return find<IdentityTranslatorType>(key); } + bool contains(const KeyType& key) const { return contains<IdentityTranslatorType>(key); } + + template<typename HashTranslator, typename T> iterator find(const T&); + template<typename HashTranslator, typename T> const_iterator find(const T&) const; + template<typename HashTranslator, typename T> bool contains(const T&) const; + + void remove(const KeyType&); + void remove(iterator); + void removeWithoutEntryConsistencyCheck(iterator); + void removeWithoutEntryConsistencyCheck(const_iterator); + void clear(); + + static bool isEmptyBucket(const ValueType& value) { return Extractor::extract(value) == KeyTraits::emptyValue(); } + static bool isDeletedBucket(const ValueType& value) { return KeyTraits::isDeletedValue(Extractor::extract(value)); } + static bool isEmptyOrDeletedBucket(const ValueType& value) { return isEmptyBucket(value) || isDeletedBucket(value); } + + ValueType* lookup(const Key& key) { return lookup<IdentityTranslatorType>(key); } + template<typename HashTranslator, typename T> ValueType* lookup(const T&); + +#if !ASSERT_DISABLED + void checkTableConsistency() const; +#else + static void checkTableConsistency() { } +#endif +#if CHECK_HASHTABLE_CONSISTENCY + void internalCheckTableConsistency() const { checkTableConsistency(); } + void internalCheckTableConsistencyExceptSize() const { checkTableConsistencyExceptSize(); } +#else + static void internalCheckTableConsistencyExceptSize() { } + static void internalCheckTableConsistency() { } +#endif + + private: + static ValueType* allocateTable(int size); + static void deallocateTable(ValueType* table, int size); + + typedef pair<ValueType*, bool> LookupType; + typedef pair<LookupType, unsigned> FullLookupType; + + LookupType lookupForWriting(const Key& key) { return lookupForWriting<IdentityTranslatorType>(key); }; + template<typename HashTranslator, typename T> FullLookupType fullLookupForWriting(const T&); + template<typename HashTranslator, typename T> LookupType lookupForWriting(const T&); + + template<typename HashTranslator, typename T> void checkKey(const T&); + + void removeAndInvalidateWithoutEntryConsistencyCheck(ValueType*); + void removeAndInvalidate(ValueType*); + void remove(ValueType*); + + bool shouldExpand() const { return (m_keyCount + m_deletedCount) * m_maxLoad >= m_tableSize; } + bool mustRehashInPlace() const { return m_keyCount * m_minLoad < m_tableSize * 2; } + bool shouldShrink() const { return m_keyCount * m_minLoad < m_tableSize && m_tableSize > KeyTraits::minimumTableSize; } + void expand(); + void shrink() { rehash(m_tableSize / 2); } + + void rehash(int newTableSize); + void reinsert(ValueType&); + + static void initializeBucket(ValueType& bucket); + static void deleteBucket(ValueType& bucket) { bucket.~ValueType(); Traits::constructDeletedValue(bucket); } + + FullLookupType makeLookupResult(ValueType* position, bool found, unsigned hash) + { return FullLookupType(LookupType(position, found), hash); } + + iterator makeIterator(ValueType* pos) { return iterator(this, pos, m_table + m_tableSize); } + const_iterator makeConstIterator(ValueType* pos) const { return const_iterator(this, pos, m_table + m_tableSize); } + iterator makeKnownGoodIterator(ValueType* pos) { return iterator(this, pos, m_table + m_tableSize, HashItemKnownGood); } + const_iterator makeKnownGoodConstIterator(ValueType* pos) const { return const_iterator(this, pos, m_table + m_tableSize, HashItemKnownGood); } + +#if !ASSERT_DISABLED + void checkTableConsistencyExceptSize() const; +#else + static void checkTableConsistencyExceptSize() { } +#endif + +#if CHECK_HASHTABLE_ITERATORS + void invalidateIterators(); +#else + static void invalidateIterators() { } +#endif + + static const int m_maxLoad = 2; + static const int m_minLoad = 6; + + ValueType* m_table; + int m_tableSize; + int m_tableSizeMask; + int m_keyCount; + int m_deletedCount; + +#if CHECK_HASHTABLE_ITERATORS + public: + // All access to m_iterators should be guarded with m_mutex. + mutable const_iterator* m_iterators; + mutable Mutex m_mutex; +#endif + }; + + template<typename Key, typename Value, typename Extractor, typename HashFunctions, typename Traits, typename KeyTraits> + inline HashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits>::HashTable() + : m_table(0) + , m_tableSize(0) + , m_tableSizeMask(0) + , m_keyCount(0) + , m_deletedCount(0) +#if CHECK_HASHTABLE_ITERATORS + , m_iterators(0) +#endif + { + } + + inline unsigned doubleHash(unsigned key) + { + key = ~key + (key >> 23); + key ^= (key << 12); + key ^= (key >> 7); + key ^= (key << 2); + key ^= (key >> 20); + return key; + } + +#if ASSERT_DISABLED + + template<typename Key, typename Value, typename Extractor, typename HashFunctions, typename Traits, typename KeyTraits> + template<typename HashTranslator, typename T> + inline void HashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits>::checkKey(const T&) + { + } + +#else + + template<typename Key, typename Value, typename Extractor, typename HashFunctions, typename Traits, typename KeyTraits> + template<typename HashTranslator, typename T> + void HashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits>::checkKey(const T& key) + { + if (!HashFunctions::safeToCompareToEmptyOrDeleted) + return; + ASSERT(!HashTranslator::equal(KeyTraits::emptyValue(), key)); + AlignedBuffer<sizeof(ValueType), WTF_ALIGN_OF(ValueType)> deletedValueBuffer; + ValueType& deletedValue = *reinterpret_cast_ptr<ValueType*>(deletedValueBuffer.buffer); + Traits::constructDeletedValue(deletedValue); + ASSERT(!HashTranslator::equal(Extractor::extract(deletedValue), key)); + } + +#endif + + template<typename Key, typename Value, typename Extractor, typename HashFunctions, typename Traits, typename KeyTraits> + template<typename HashTranslator, typename T> + inline Value* HashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits>::lookup(const T& key) + { + checkKey<HashTranslator>(key); + + int k = 0; + int sizeMask = m_tableSizeMask; + ValueType* table = m_table; + unsigned h = HashTranslator::hash(key); + int i = h & sizeMask; + + if (!table) + return 0; + +#if DUMP_HASHTABLE_STATS + atomicIncrement(&HashTableStats::numAccesses); + int probeCount = 0; +#endif + + while (1) { + ValueType* entry = table + i; + + // we count on the compiler to optimize out this branch + if (HashFunctions::safeToCompareToEmptyOrDeleted) { + if (HashTranslator::equal(Extractor::extract(*entry), key)) + return entry; + + if (isEmptyBucket(*entry)) + return 0; + } else { + if (isEmptyBucket(*entry)) + return 0; + + if (!isDeletedBucket(*entry) && HashTranslator::equal(Extractor::extract(*entry), key)) + return entry; + } +#if DUMP_HASHTABLE_STATS + ++probeCount; + HashTableStats::recordCollisionAtCount(probeCount); +#endif + if (k == 0) + k = 1 | doubleHash(h); + i = (i + k) & sizeMask; + } + } + + template<typename Key, typename Value, typename Extractor, typename HashFunctions, typename Traits, typename KeyTraits> + template<typename HashTranslator, typename T> + inline typename HashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits>::LookupType HashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits>::lookupForWriting(const T& key) + { + ASSERT(m_table); + checkKey<HashTranslator>(key); + + int k = 0; + ValueType* table = m_table; + int sizeMask = m_tableSizeMask; + unsigned h = HashTranslator::hash(key); + int i = h & sizeMask; + +#if DUMP_HASHTABLE_STATS + atomicIncrement(&HashTableStats::numAccesses); + int probeCount = 0; +#endif + + ValueType* deletedEntry = 0; + + while (1) { + ValueType* entry = table + i; + + // we count on the compiler to optimize out this branch + if (HashFunctions::safeToCompareToEmptyOrDeleted) { + if (isEmptyBucket(*entry)) + return LookupType(deletedEntry ? deletedEntry : entry, false); + + if (HashTranslator::equal(Extractor::extract(*entry), key)) + return LookupType(entry, true); + + if (isDeletedBucket(*entry)) + deletedEntry = entry; + } else { + if (isEmptyBucket(*entry)) + return LookupType(deletedEntry ? deletedEntry : entry, false); + + if (isDeletedBucket(*entry)) + deletedEntry = entry; + else if (HashTranslator::equal(Extractor::extract(*entry), key)) + return LookupType(entry, true); + } +#if DUMP_HASHTABLE_STATS + ++probeCount; + HashTableStats::recordCollisionAtCount(probeCount); +#endif + if (k == 0) + k = 1 | doubleHash(h); + i = (i + k) & sizeMask; + } + } + + template<typename Key, typename Value, typename Extractor, typename HashFunctions, typename Traits, typename KeyTraits> + template<typename HashTranslator, typename T> + inline typename HashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits>::FullLookupType HashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits>::fullLookupForWriting(const T& key) + { + ASSERT(m_table); + checkKey<HashTranslator>(key); + + int k = 0; + ValueType* table = m_table; + int sizeMask = m_tableSizeMask; + unsigned h = HashTranslator::hash(key); + int i = h & sizeMask; + +#if DUMP_HASHTABLE_STATS + atomicIncrement(&HashTableStats::numAccesses); + int probeCount = 0; +#endif + + ValueType* deletedEntry = 0; + + while (1) { + ValueType* entry = table + i; + + // we count on the compiler to optimize out this branch + if (HashFunctions::safeToCompareToEmptyOrDeleted) { + if (isEmptyBucket(*entry)) + return makeLookupResult(deletedEntry ? deletedEntry : entry, false, h); + + if (HashTranslator::equal(Extractor::extract(*entry), key)) + return makeLookupResult(entry, true, h); + + if (isDeletedBucket(*entry)) + deletedEntry = entry; + } else { + if (isEmptyBucket(*entry)) + return makeLookupResult(deletedEntry ? deletedEntry : entry, false, h); + + if (isDeletedBucket(*entry)) + deletedEntry = entry; + else if (HashTranslator::equal(Extractor::extract(*entry), key)) + return makeLookupResult(entry, true, h); + } +#if DUMP_HASHTABLE_STATS + ++probeCount; + HashTableStats::recordCollisionAtCount(probeCount); +#endif + if (k == 0) + k = 1 | doubleHash(h); + i = (i + k) & sizeMask; + } + } + + template<bool emptyValueIsZero> struct HashTableBucketInitializer; + + template<> struct HashTableBucketInitializer<false> { + template<typename Traits, typename Value> static void initialize(Value& bucket) + { + new (NotNull, &bucket) Value(Traits::emptyValue()); + } + }; + + template<> struct HashTableBucketInitializer<true> { + template<typename Traits, typename Value> static void initialize(Value& bucket) + { + // This initializes the bucket without copying the empty value. + // That makes it possible to use this with types that don't support copying. + // The memset to 0 looks like a slow operation but is optimized by the compilers. + memset(&bucket, 0, sizeof(bucket)); + } + }; + + template<typename Key, typename Value, typename Extractor, typename HashFunctions, typename Traits, typename KeyTraits> + inline void HashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits>::initializeBucket(ValueType& bucket) + { + HashTableBucketInitializer<Traits::emptyValueIsZero>::template initialize<Traits>(bucket); + } + + template<typename Key, typename Value, typename Extractor, typename HashFunctions, typename Traits, typename KeyTraits> + template<typename HashTranslator, typename T, typename Extra> + inline pair<typename HashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits>::iterator, bool> HashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits>::add(const T& key, const Extra& extra) + { + checkKey<HashTranslator>(key); + + invalidateIterators(); + + if (!m_table) + expand(); + + internalCheckTableConsistency(); + + ASSERT(m_table); + + int k = 0; + ValueType* table = m_table; + int sizeMask = m_tableSizeMask; + unsigned h = HashTranslator::hash(key); + int i = h & sizeMask; + +#if DUMP_HASHTABLE_STATS + atomicIncrement(&HashTableStats::numAccesses); + int probeCount = 0; +#endif + + ValueType* deletedEntry = 0; + ValueType* entry; + while (1) { + entry = table + i; + + // we count on the compiler to optimize out this branch + if (HashFunctions::safeToCompareToEmptyOrDeleted) { + if (isEmptyBucket(*entry)) + break; + + if (HashTranslator::equal(Extractor::extract(*entry), key)) + return std::make_pair(makeKnownGoodIterator(entry), false); + + if (isDeletedBucket(*entry)) + deletedEntry = entry; + } else { + if (isEmptyBucket(*entry)) + break; + + if (isDeletedBucket(*entry)) + deletedEntry = entry; + else if (HashTranslator::equal(Extractor::extract(*entry), key)) + return std::make_pair(makeKnownGoodIterator(entry), false); + } +#if DUMP_HASHTABLE_STATS + ++probeCount; + HashTableStats::recordCollisionAtCount(probeCount); +#endif + if (k == 0) + k = 1 | doubleHash(h); + i = (i + k) & sizeMask; + } + + if (deletedEntry) { + initializeBucket(*deletedEntry); + entry = deletedEntry; + --m_deletedCount; + } + + HashTranslator::translate(*entry, key, extra); + + ++m_keyCount; + + if (shouldExpand()) { + // FIXME: This makes an extra copy on expand. Probably not that bad since + // expand is rare, but would be better to have a version of expand that can + // follow a pivot entry and return the new position. + KeyType enteredKey = Extractor::extract(*entry); + expand(); + pair<iterator, bool> p = std::make_pair(find(enteredKey), true); + ASSERT(p.first != end()); + return p; + } + + internalCheckTableConsistency(); + + return std::make_pair(makeKnownGoodIterator(entry), true); + } + + template<typename Key, typename Value, typename Extractor, typename HashFunctions, typename Traits, typename KeyTraits> + template<typename HashTranslator, typename T, typename Extra> + inline pair<typename HashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits>::iterator, bool> HashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits>::addPassingHashCode(const T& key, const Extra& extra) + { + checkKey<HashTranslator>(key); + + invalidateIterators(); + + if (!m_table) + expand(); + + internalCheckTableConsistency(); + + FullLookupType lookupResult = fullLookupForWriting<HashTranslator>(key); + + ValueType* entry = lookupResult.first.first; + bool found = lookupResult.first.second; + unsigned h = lookupResult.second; + + if (found) + return std::make_pair(makeKnownGoodIterator(entry), false); + + if (isDeletedBucket(*entry)) { + initializeBucket(*entry); + --m_deletedCount; + } + + HashTranslator::translate(*entry, key, extra, h); + ++m_keyCount; + if (shouldExpand()) { + // FIXME: This makes an extra copy on expand. Probably not that bad since + // expand is rare, but would be better to have a version of expand that can + // follow a pivot entry and return the new position. + KeyType enteredKey = Extractor::extract(*entry); + expand(); + pair<iterator, bool> p = std::make_pair(find(enteredKey), true); + ASSERT(p.first != end()); + return p; + } + + internalCheckTableConsistency(); + + return std::make_pair(makeKnownGoodIterator(entry), true); + } + + template<typename Key, typename Value, typename Extractor, typename HashFunctions, typename Traits, typename KeyTraits> + inline void HashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits>::reinsert(ValueType& entry) + { + ASSERT(m_table); + ASSERT(!lookupForWriting(Extractor::extract(entry)).second); + ASSERT(!isDeletedBucket(*(lookupForWriting(Extractor::extract(entry)).first))); +#if DUMP_HASHTABLE_STATS + atomicIncrement(&HashTableStats::numReinserts); +#endif + + Mover<ValueType, Traits::needsDestruction>::move(entry, *lookupForWriting(Extractor::extract(entry)).first); + } + + template<typename Key, typename Value, typename Extractor, typename HashFunctions, typename Traits, typename KeyTraits> + template <typename HashTranslator, typename T> + typename HashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits>::iterator HashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits>::find(const T& key) + { + if (!m_table) + return end(); + + ValueType* entry = lookup<HashTranslator>(key); + if (!entry) + return end(); + + return makeKnownGoodIterator(entry); + } + + template<typename Key, typename Value, typename Extractor, typename HashFunctions, typename Traits, typename KeyTraits> + template <typename HashTranslator, typename T> + typename HashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits>::const_iterator HashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits>::find(const T& key) const + { + if (!m_table) + return end(); + + ValueType* entry = const_cast<HashTable*>(this)->lookup<HashTranslator>(key); + if (!entry) + return end(); + + return makeKnownGoodConstIterator(entry); + } + + template<typename Key, typename Value, typename Extractor, typename HashFunctions, typename Traits, typename KeyTraits> + template <typename HashTranslator, typename T> + bool HashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits>::contains(const T& key) const + { + if (!m_table) + return false; + + return const_cast<HashTable*>(this)->lookup<HashTranslator>(key); + } + + template<typename Key, typename Value, typename Extractor, typename HashFunctions, typename Traits, typename KeyTraits> + void HashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits>::removeAndInvalidateWithoutEntryConsistencyCheck(ValueType* pos) + { + invalidateIterators(); + remove(pos); + } + + template<typename Key, typename Value, typename Extractor, typename HashFunctions, typename Traits, typename KeyTraits> + void HashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits>::removeAndInvalidate(ValueType* pos) + { + invalidateIterators(); + internalCheckTableConsistency(); + remove(pos); + } + + template<typename Key, typename Value, typename Extractor, typename HashFunctions, typename Traits, typename KeyTraits> + void HashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits>::remove(ValueType* pos) + { +#if DUMP_HASHTABLE_STATS + atomicIncrement(&HashTableStats::numRemoves); +#endif + + deleteBucket(*pos); + ++m_deletedCount; + --m_keyCount; + + if (shouldShrink()) + shrink(); + + internalCheckTableConsistency(); + } + + template<typename Key, typename Value, typename Extractor, typename HashFunctions, typename Traits, typename KeyTraits> + inline void HashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits>::remove(iterator it) + { + if (it == end()) + return; + + removeAndInvalidate(const_cast<ValueType*>(it.m_iterator.m_position)); + } + + template<typename Key, typename Value, typename Extractor, typename HashFunctions, typename Traits, typename KeyTraits> + inline void HashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits>::removeWithoutEntryConsistencyCheck(iterator it) + { + if (it == end()) + return; + + removeAndInvalidateWithoutEntryConsistencyCheck(const_cast<ValueType*>(it.m_iterator.m_position)); + } + + template<typename Key, typename Value, typename Extractor, typename HashFunctions, typename Traits, typename KeyTraits> + inline void HashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits>::removeWithoutEntryConsistencyCheck(const_iterator it) + { + if (it == end()) + return; + + removeAndInvalidateWithoutEntryConsistencyCheck(const_cast<ValueType*>(it.m_position)); + } + + template<typename Key, typename Value, typename Extractor, typename HashFunctions, typename Traits, typename KeyTraits> + inline void HashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits>::remove(const KeyType& key) + { + remove(find(key)); + } + + template<typename Key, typename Value, typename Extractor, typename HashFunctions, typename Traits, typename KeyTraits> + Value* HashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits>::allocateTable(int size) + { + // would use a template member function with explicit specializations here, but + // gcc doesn't appear to support that + if (Traits::emptyValueIsZero) + return static_cast<ValueType*>(fastZeroedMalloc(size * sizeof(ValueType))); + ValueType* result = static_cast<ValueType*>(fastMalloc(size * sizeof(ValueType))); + for (int i = 0; i < size; i++) + initializeBucket(result[i]); + return result; + } + + template<typename Key, typename Value, typename Extractor, typename HashFunctions, typename Traits, typename KeyTraits> + void HashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits>::deallocateTable(ValueType* table, int size) + { + if (Traits::needsDestruction) { + for (int i = 0; i < size; ++i) { + if (!isDeletedBucket(table[i])) + table[i].~ValueType(); + } + } + fastFree(table); + } + + template<typename Key, typename Value, typename Extractor, typename HashFunctions, typename Traits, typename KeyTraits> + void HashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits>::expand() + { + int newSize; + if (m_tableSize == 0) + newSize = KeyTraits::minimumTableSize; + else if (mustRehashInPlace()) + newSize = m_tableSize; + else + newSize = m_tableSize * 2; + + rehash(newSize); + } + + template<typename Key, typename Value, typename Extractor, typename HashFunctions, typename Traits, typename KeyTraits> + void HashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits>::rehash(int newTableSize) + { + internalCheckTableConsistencyExceptSize(); + + int oldTableSize = m_tableSize; + ValueType* oldTable = m_table; + +#if DUMP_HASHTABLE_STATS + if (oldTableSize != 0) + atomicIncrement(&HashTableStats::numRehashes); +#endif + + m_tableSize = newTableSize; + m_tableSizeMask = newTableSize - 1; + m_table = allocateTable(newTableSize); + + for (int i = 0; i != oldTableSize; ++i) + if (!isEmptyOrDeletedBucket(oldTable[i])) + reinsert(oldTable[i]); + + m_deletedCount = 0; + + deallocateTable(oldTable, oldTableSize); + + internalCheckTableConsistency(); + } + + template<typename Key, typename Value, typename Extractor, typename HashFunctions, typename Traits, typename KeyTraits> + void HashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits>::clear() + { + invalidateIterators(); + deallocateTable(m_table, m_tableSize); + m_table = 0; + m_tableSize = 0; + m_tableSizeMask = 0; + m_keyCount = 0; + } + + template<typename Key, typename Value, typename Extractor, typename HashFunctions, typename Traits, typename KeyTraits> + HashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits>::HashTable(const HashTable& other) + : m_table(0) + , m_tableSize(0) + , m_tableSizeMask(0) + , m_keyCount(0) + , m_deletedCount(0) +#if CHECK_HASHTABLE_ITERATORS + , m_iterators(0) +#endif + { + // Copy the hash table the dumb way, by adding each element to the new table. + // It might be more efficient to copy the table slots, but it's not clear that efficiency is needed. + const_iterator end = other.end(); + for (const_iterator it = other.begin(); it != end; ++it) + add(*it); + } + + template<typename Key, typename Value, typename Extractor, typename HashFunctions, typename Traits, typename KeyTraits> + void HashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits>::swap(HashTable& other) + { + invalidateIterators(); + other.invalidateIterators(); + + ValueType* tmp_table = m_table; + m_table = other.m_table; + other.m_table = tmp_table; + + int tmp_tableSize = m_tableSize; + m_tableSize = other.m_tableSize; + other.m_tableSize = tmp_tableSize; + + int tmp_tableSizeMask = m_tableSizeMask; + m_tableSizeMask = other.m_tableSizeMask; + other.m_tableSizeMask = tmp_tableSizeMask; + + int tmp_keyCount = m_keyCount; + m_keyCount = other.m_keyCount; + other.m_keyCount = tmp_keyCount; + + int tmp_deletedCount = m_deletedCount; + m_deletedCount = other.m_deletedCount; + other.m_deletedCount = tmp_deletedCount; + } + + template<typename Key, typename Value, typename Extractor, typename HashFunctions, typename Traits, typename KeyTraits> + HashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits>& HashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits>::operator=(const HashTable& other) + { + HashTable tmp(other); + swap(tmp); + return *this; + } + +#if !ASSERT_DISABLED + + template<typename Key, typename Value, typename Extractor, typename HashFunctions, typename Traits, typename KeyTraits> + void HashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits>::checkTableConsistency() const + { + checkTableConsistencyExceptSize(); + ASSERT(!m_table || !shouldExpand()); + ASSERT(!shouldShrink()); + } + + template<typename Key, typename Value, typename Extractor, typename HashFunctions, typename Traits, typename KeyTraits> + void HashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits>::checkTableConsistencyExceptSize() const + { + if (!m_table) + return; + + int count = 0; + int deletedCount = 0; + for (int j = 0; j < m_tableSize; ++j) { + ValueType* entry = m_table + j; + if (isEmptyBucket(*entry)) + continue; + + if (isDeletedBucket(*entry)) { + ++deletedCount; + continue; + } + + const_iterator it = find(Extractor::extract(*entry)); + ASSERT(entry == it.m_position); + ++count; + + ValueCheck<Key>::checkConsistency(it->first); + } + + ASSERT(count == m_keyCount); + ASSERT(deletedCount == m_deletedCount); + ASSERT(m_tableSize >= KeyTraits::minimumTableSize); + ASSERT(m_tableSizeMask); + ASSERT(m_tableSize == m_tableSizeMask + 1); + } + +#endif // ASSERT_DISABLED + +#if CHECK_HASHTABLE_ITERATORS + + template<typename Key, typename Value, typename Extractor, typename HashFunctions, typename Traits, typename KeyTraits> + void HashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits>::invalidateIterators() + { + MutexLocker lock(m_mutex); + const_iterator* next; + for (const_iterator* p = m_iterators; p; p = next) { + next = p->m_next; + p->m_table = 0; + p->m_next = 0; + p->m_previous = 0; + } + m_iterators = 0; + } + + template<typename Key, typename Value, typename Extractor, typename HashFunctions, typename Traits, typename KeyTraits> + void addIterator(const HashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits>* table, + HashTableConstIterator<Key, Value, Extractor, HashFunctions, Traits, KeyTraits>* it) + { + it->m_table = table; + it->m_previous = 0; + + // Insert iterator at head of doubly-linked list of iterators. + if (!table) { + it->m_next = 0; + } else { + MutexLocker lock(table->m_mutex); + ASSERT(table->m_iterators != it); + it->m_next = table->m_iterators; + table->m_iterators = it; + if (it->m_next) { + ASSERT(!it->m_next->m_previous); + it->m_next->m_previous = it; + } + } + } + + template<typename Key, typename Value, typename Extractor, typename HashFunctions, typename Traits, typename KeyTraits> + void removeIterator(HashTableConstIterator<Key, Value, Extractor, HashFunctions, Traits, KeyTraits>* it) + { + typedef HashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits> HashTableType; + typedef HashTableConstIterator<Key, Value, Extractor, HashFunctions, Traits, KeyTraits> const_iterator; + + // Delete iterator from doubly-linked list of iterators. + if (!it->m_table) { + ASSERT(!it->m_next); + ASSERT(!it->m_previous); + } else { + MutexLocker lock(it->m_table->m_mutex); + if (it->m_next) { + ASSERT(it->m_next->m_previous == it); + it->m_next->m_previous = it->m_previous; + } + if (it->m_previous) { + ASSERT(it->m_table->m_iterators != it); + ASSERT(it->m_previous->m_next == it); + it->m_previous->m_next = it->m_next; + } else { + ASSERT(it->m_table->m_iterators == it); + it->m_table->m_iterators = it->m_next; + } + } + + it->m_table = 0; + it->m_next = 0; + it->m_previous = 0; + } + +#endif // CHECK_HASHTABLE_ITERATORS + + // iterator adapters + + template<typename HashTableType, typename ValueType> struct HashTableConstIteratorAdapter { + HashTableConstIteratorAdapter() {} + HashTableConstIteratorAdapter(const typename HashTableType::const_iterator& impl) : m_impl(impl) {} + + const ValueType* get() const { return (const ValueType*)m_impl.get(); } + const ValueType& operator*() const { return *get(); } + const ValueType* operator->() const { return get(); } + + HashTableConstIteratorAdapter& operator++() { ++m_impl; return *this; } + // postfix ++ intentionally omitted + + typename HashTableType::const_iterator m_impl; + }; + + template<typename HashTableType, typename ValueType> struct HashTableIteratorAdapter { + HashTableIteratorAdapter() {} + HashTableIteratorAdapter(const typename HashTableType::iterator& impl) : m_impl(impl) {} + + ValueType* get() const { return (ValueType*)m_impl.get(); } + ValueType& operator*() const { return *get(); } + ValueType* operator->() const { return get(); } + + HashTableIteratorAdapter& operator++() { ++m_impl; return *this; } + // postfix ++ intentionally omitted + + operator HashTableConstIteratorAdapter<HashTableType, ValueType>() { + typename HashTableType::const_iterator i = m_impl; + return i; + } + + typename HashTableType::iterator m_impl; + }; + + template<typename T, typename U> + inline bool operator==(const HashTableConstIteratorAdapter<T, U>& a, const HashTableConstIteratorAdapter<T, U>& b) + { + return a.m_impl == b.m_impl; + } + + template<typename T, typename U> + inline bool operator!=(const HashTableConstIteratorAdapter<T, U>& a, const HashTableConstIteratorAdapter<T, U>& b) + { + return a.m_impl != b.m_impl; + } + + template<typename T, typename U> + inline bool operator==(const HashTableIteratorAdapter<T, U>& a, const HashTableIteratorAdapter<T, U>& b) + { + return a.m_impl == b.m_impl; + } + + template<typename T, typename U> + inline bool operator!=(const HashTableIteratorAdapter<T, U>& a, const HashTableIteratorAdapter<T, U>& b) + { + return a.m_impl != b.m_impl; + } + + // All 4 combinations of ==, != and Const,non const. + template<typename T, typename U> + inline bool operator==(const HashTableConstIteratorAdapter<T, U>& a, const HashTableIteratorAdapter<T, U>& b) + { + return a.m_impl == b.m_impl; + } + + template<typename T, typename U> + inline bool operator!=(const HashTableConstIteratorAdapter<T, U>& a, const HashTableIteratorAdapter<T, U>& b) + { + return a.m_impl != b.m_impl; + } + + template<typename T, typename U> + inline bool operator==(const HashTableIteratorAdapter<T, U>& a, const HashTableConstIteratorAdapter<T, U>& b) + { + return a.m_impl == b.m_impl; + } + + template<typename T, typename U> + inline bool operator!=(const HashTableIteratorAdapter<T, U>& a, const HashTableConstIteratorAdapter<T, U>& b) + { + return a.m_impl != b.m_impl; + } + +} // namespace WTF + +#include "HashIterators.h" + +#endif // WTF_HashTable_h diff --git a/Source/JavaScriptCore/wtf/HashTraits.h b/Source/JavaScriptCore/wtf/HashTraits.h new file mode 100644 index 000000000..12e6b0699 --- /dev/null +++ b/Source/JavaScriptCore/wtf/HashTraits.h @@ -0,0 +1,167 @@ +/* + * Copyright (C) 2005, 2006, 2007, 2008, 2011 Apple Inc. All rights reserved. + * + * This library 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 library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#ifndef WTF_HashTraits_h +#define WTF_HashTraits_h + +#include "HashFunctions.h" +#include "StdLibExtras.h" +#include "TypeTraits.h" +#include <utility> +#include <limits> + +namespace WTF { + + class String; + + template<typename T> class OwnPtr; + template<typename T> class PassOwnPtr; + + using std::pair; + using std::make_pair; + + template<typename T> struct HashTraits; + + template<bool isInteger, typename T> struct GenericHashTraitsBase; + + template<typename T> struct GenericHashTraitsBase<false, T> { + static const bool emptyValueIsZero = false; + static const bool needsDestruction = true; + static const int minimumTableSize = 64; + }; + + // Default integer traits disallow both 0 and -1 as keys (max value instead of -1 for unsigned). + template<typename T> struct GenericHashTraitsBase<true, T> : GenericHashTraitsBase<false, T> { + static const bool emptyValueIsZero = true; + static const bool needsDestruction = false; + static void constructDeletedValue(T& slot) { slot = static_cast<T>(-1); } + static bool isDeletedValue(T value) { return value == static_cast<T>(-1); } + }; + + template<typename T> struct GenericHashTraits : GenericHashTraitsBase<IsInteger<T>::value, T> { + typedef T TraitType; + + static T emptyValue() { return T(); } + + // Type for functions that take ownership, such as add. + // The store function either not be called or called once to store something passed in. + // The value passed to the store function will be either PassInType or PassInType&. + typedef const T& PassInType; + static void store(const T& value, T& storage) { storage = value; } + + // Type for return value of functions that transfer ownership, such as take. + typedef T PassOutType; + static PassOutType passOut(const T& value) { return value; } + + // Type for return value of functions that do not transfer ownership, such as get. + // FIXME: We could change this type to const T& for better performance if we figured out + // a way to handle the return value from emptyValue, which is a temporary. + typedef T PeekType; + static PeekType peek(const T& value) { return value; } + }; + + template<typename T> struct HashTraits : GenericHashTraits<T> { }; + + template<typename T> struct FloatHashTraits : GenericHashTraits<T> { + static const bool needsDestruction = false; + static T emptyValue() { return std::numeric_limits<T>::infinity(); } + static void constructDeletedValue(T& slot) { slot = -std::numeric_limits<T>::infinity(); } + static bool isDeletedValue(T value) { return value == -std::numeric_limits<T>::infinity(); } + }; + + template<> struct HashTraits<float> : FloatHashTraits<float> { }; + template<> struct HashTraits<double> : FloatHashTraits<double> { }; + + // Default unsigned traits disallow both 0 and max as keys -- use these traits to allow zero and disallow max - 1. + template<typename T> struct UnsignedWithZeroKeyHashTraits : GenericHashTraits<T> { + static const bool emptyValueIsZero = false; + static const bool needsDestruction = false; + static T emptyValue() { return std::numeric_limits<T>::max(); } + static void constructDeletedValue(T& slot) { slot = std::numeric_limits<T>::max() - 1; } + static bool isDeletedValue(T value) { return value == std::numeric_limits<T>::max() - 1; } + }; + + template<typename P> struct HashTraits<P*> : GenericHashTraits<P*> { + static const bool emptyValueIsZero = true; + static const bool needsDestruction = false; + static void constructDeletedValue(P*& slot) { slot = reinterpret_cast<P*>(-1); } + static bool isDeletedValue(P* value) { return value == reinterpret_cast<P*>(-1); } + }; + + template<typename T> struct SimpleClassHashTraits : GenericHashTraits<T> { + static const bool emptyValueIsZero = true; + static void constructDeletedValue(T& slot) { new (NotNull, &slot) T(HashTableDeletedValue); } + static bool isDeletedValue(const T& value) { return value.isHashTableDeletedValue(); } + }; + + template<typename P> struct HashTraits<OwnPtr<P> > : SimpleClassHashTraits<OwnPtr<P> > { + static std::nullptr_t emptyValue() { return nullptr; } + + typedef PassOwnPtr<P> PassInType; + static void store(PassOwnPtr<P> value, OwnPtr<P>& storage) { storage = value; } + + typedef PassOwnPtr<P> PassOutType; + static PassOwnPtr<P> passOut(OwnPtr<P>& value) { return value.release(); } + static PassOwnPtr<P> passOut(std::nullptr_t) { return nullptr; } + + typedef typename OwnPtr<P>::PtrType PeekType; + static PeekType peek(const OwnPtr<P>& value) { return value.get(); } + static PeekType peek(std::nullptr_t) { return 0; } + }; + + template<typename P> struct HashTraits<RefPtr<P> > : SimpleClassHashTraits<RefPtr<P> > { + typedef PassRefPtr<P> PassInType; + static void store(PassRefPtr<P> value, RefPtr<P>& storage) { storage = value; } + + // FIXME: We should change PassOutType to PassRefPtr for better performance. + // FIXME: We should consider changing PeekType to a raw pointer for better performance, + // but then callers won't need to call get; doing so will require updating many call sites. + }; + + template<> struct HashTraits<String> : SimpleClassHashTraits<String> { }; + + // special traits for pairs, helpful for their use in HashMap implementation + + template<typename FirstTraitsArg, typename SecondTraitsArg> + struct PairHashTraits : GenericHashTraits<pair<typename FirstTraitsArg::TraitType, typename SecondTraitsArg::TraitType> > { + typedef FirstTraitsArg FirstTraits; + typedef SecondTraitsArg SecondTraits; + typedef pair<typename FirstTraits::TraitType, typename SecondTraits::TraitType> TraitType; + + static const bool emptyValueIsZero = FirstTraits::emptyValueIsZero && SecondTraits::emptyValueIsZero; + static TraitType emptyValue() { return make_pair(FirstTraits::emptyValue(), SecondTraits::emptyValue()); } + + static const bool needsDestruction = FirstTraits::needsDestruction || SecondTraits::needsDestruction; + + static const int minimumTableSize = FirstTraits::minimumTableSize; + + static void constructDeletedValue(TraitType& slot) { FirstTraits::constructDeletedValue(slot.first); } + static bool isDeletedValue(const TraitType& value) { return FirstTraits::isDeletedValue(value.first); } + }; + + template<typename First, typename Second> + struct HashTraits<pair<First, Second> > : public PairHashTraits<HashTraits<First>, HashTraits<Second> > { }; + +} // namespace WTF + +using WTF::HashTraits; +using WTF::PairHashTraits; + +#endif // WTF_HashTraits_h diff --git a/Source/JavaScriptCore/wtf/HexNumber.h b/Source/JavaScriptCore/wtf/HexNumber.h new file mode 100644 index 000000000..8fd60323b --- /dev/null +++ b/Source/JavaScriptCore/wtf/HexNumber.h @@ -0,0 +1,107 @@ +/* + * Copyright (C) 2011 Research In Motion Limited. All rights reserved. + * + * This library 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 library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef HexNumber_h +#define HexNumber_h + +#include <wtf/text/StringConcatenate.h> + +namespace WTF { + +enum HexConversionMode { + Lowercase, + Uppercase +}; + +namespace Internal { + +static const char* hexDigitsForMode(HexConversionMode mode) +{ + static const char lowerHexDigits[17] = "0123456789abcdef"; + static const char upperHexDigits[17] = "0123456789ABCDEF"; + return mode == Lowercase ? lowerHexDigits : upperHexDigits; +} + +}; // namespace Internal + +template<typename T> +inline void appendByteAsHex(unsigned char byte, T& destination, HexConversionMode mode = Uppercase) +{ + const char* hexDigits = Internal::hexDigitsForMode(mode); + destination.append(hexDigits[byte >> 4]); + destination.append(hexDigits[byte & 0xF]); +} + +template<typename T> +inline void placeByteAsHexCompressIfPossible(unsigned char byte, T& destination, unsigned& index, HexConversionMode mode = Uppercase) +{ + const char* hexDigits = Internal::hexDigitsForMode(mode); + if (byte >= 0x10) + destination[index++] = hexDigits[byte >> 4]; + destination[index++] = hexDigits[byte & 0xF]; +} + +template<typename T> +inline void placeByteAsHex(unsigned char byte, T& destination, HexConversionMode mode = Uppercase) +{ + const char* hexDigits = Internal::hexDigitsForMode(mode); + *destination++ = hexDigits[byte >> 4]; + *destination++ = hexDigits[byte & 0xF]; +} + +template<typename T> +inline void appendUnsignedAsHex(unsigned number, T& destination, HexConversionMode mode = Uppercase) +{ + const char* hexDigits = Internal::hexDigitsForMode(mode); + Vector<UChar, 8> result; + do { + result.prepend(hexDigits[number % 16]); + number >>= 4; + } while (number > 0); + + destination.append(result.data(), result.size()); +} + +// Same as appendUnsignedAsHex, but using exactly 'desiredDigits' for the conversion. +template<typename T> +inline void appendUnsignedAsHexFixedSize(unsigned number, T& destination, unsigned desiredDigits, HexConversionMode mode = Uppercase) +{ + ASSERT(desiredDigits); + + const char* hexDigits = Internal::hexDigitsForMode(mode); + Vector<UChar, 8> result; + do { + result.prepend(hexDigits[number % 16]); + number >>= 4; + } while (result.size() < desiredDigits); + + ASSERT(result.size() == desiredDigits); + destination.append(result.data(), result.size()); +} + +} // namespace WTF + +using WTF::appendByteAsHex; +using WTF::appendUnsignedAsHex; +using WTF::appendUnsignedAsHexFixedSize; +using WTF::placeByteAsHex; +using WTF::placeByteAsHexCompressIfPossible; +using WTF::Lowercase; + +#endif // HexNumber_h diff --git a/Source/JavaScriptCore/wtf/InlineASM.h b/Source/JavaScriptCore/wtf/InlineASM.h new file mode 100644 index 000000000..c8a93f99c --- /dev/null +++ b/Source/JavaScriptCore/wtf/InlineASM.h @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2011 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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 APPLE INC. ``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 APPLE INC. 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. + */ + +#ifndef InlineASM_h +#define InlineASM_h + +#include <wtf/Platform.h> + +/* asm directive helpers */ + +#if OS(DARWIN) || (OS(WINDOWS) && CPU(X86)) +#define SYMBOL_STRING(name) "_" #name +#else +#define SYMBOL_STRING(name) #name +#endif + +#if OS(IOS) +#define THUMB_FUNC_PARAM(name) SYMBOL_STRING(name) +#else +#define THUMB_FUNC_PARAM(name) +#endif + +#if (OS(LINUX) || OS(FREEBSD)) && CPU(X86_64) +#define SYMBOL_STRING_RELOCATION(name) #name "@plt" +#elif CPU(X86) && COMPILER(MINGW) +#define SYMBOL_STRING_RELOCATION(name) "@" #name "@4" +#else +#define SYMBOL_STRING_RELOCATION(name) SYMBOL_STRING(name) +#endif + +#if OS(DARWIN) + // Mach-O platform +#define HIDE_SYMBOL(name) ".private_extern _" #name +#elif OS(AIX) + // IBM's own file format +#define HIDE_SYMBOL(name) ".lglobl " #name +#elif OS(LINUX) \ + || OS(FREEBSD) \ + || OS(OPENBSD) \ + || OS(SOLARIS) \ + || (OS(HPUX) && CPU(IA64)) \ + || OS(NETBSD) + // ELF platform +#define HIDE_SYMBOL(name) ".hidden " #name +#else +#define HIDE_SYMBOL(name) +#endif + +#endif // InlineASM_h diff --git a/Source/JavaScriptCore/wtf/Int16Array.h b/Source/JavaScriptCore/wtf/Int16Array.h new file mode 100644 index 000000000..34a52de90 --- /dev/null +++ b/Source/JavaScriptCore/wtf/Int16Array.h @@ -0,0 +1,93 @@ +/* + * Copyright (C) 2009 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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 APPLE COMPUTER, INC. ``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 APPLE COMPUTER, INC. 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. + */ + +#ifndef Int16Array_h +#define Int16Array_h + +#include "IntegralTypedArrayBase.h" + +namespace WTF { + +class ArrayBuffer; + +class Int16Array : public IntegralTypedArrayBase<short> { +public: + static inline PassRefPtr<Int16Array> create(unsigned length); + static inline PassRefPtr<Int16Array> create(short* array, unsigned length); + static inline PassRefPtr<Int16Array> create(PassRefPtr<ArrayBuffer>, unsigned byteOffset, unsigned length); + + // Can’t use "using" here due to a bug in the RVCT compiler. + bool set(TypedArrayBase<short>* array, unsigned offset) { return TypedArrayBase<short>::set(array, offset); } + void set(unsigned index, double value) { IntegralTypedArrayBase<short>::set(index, value); } + + inline PassRefPtr<Int16Array> subarray(int start) const; + inline PassRefPtr<Int16Array> subarray(int start, int end) const; + +private: + inline Int16Array(PassRefPtr<ArrayBuffer>, + unsigned byteOffset, + unsigned length); + // Make constructor visible to superclass. + friend class TypedArrayBase<short>; + + // Overridden from ArrayBufferView. + virtual bool isShortArray() const { return true; } +}; + +PassRefPtr<Int16Array> Int16Array::create(unsigned length) +{ + return TypedArrayBase<short>::create<Int16Array>(length); +} + +PassRefPtr<Int16Array> Int16Array::create(short* array, unsigned length) +{ + return TypedArrayBase<short>::create<Int16Array>(array, length); +} + +PassRefPtr<Int16Array> Int16Array::create(PassRefPtr<ArrayBuffer> buffer, unsigned byteOffset, unsigned length) +{ + return TypedArrayBase<short>::create<Int16Array>(buffer, byteOffset, length); +} + +Int16Array::Int16Array(PassRefPtr<ArrayBuffer> buffer, unsigned byteOffset, unsigned length) + : IntegralTypedArrayBase<short>(buffer, byteOffset, length) +{ +} + +PassRefPtr<Int16Array> Int16Array::subarray(int start) const +{ + return subarray(start, length()); +} + +PassRefPtr<Int16Array> Int16Array::subarray(int start, int end) const +{ + return subarrayImpl<Int16Array>(start, end); +} + +} // namespace WTF + +using WTF::Int16Array; + +#endif // Int16Array_h diff --git a/Source/JavaScriptCore/wtf/Int32Array.h b/Source/JavaScriptCore/wtf/Int32Array.h new file mode 100644 index 000000000..0b61457b3 --- /dev/null +++ b/Source/JavaScriptCore/wtf/Int32Array.h @@ -0,0 +1,92 @@ +/* + * Copyright (C) 2009 Apple Inc. All rights reserved. + * Copyright (C) 2009 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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 APPLE COMPUTER, INC. ``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 APPLE COMPUTER, INC. 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. + */ + +#ifndef Int32Array_h +#define Int32Array_h + +#include "IntegralTypedArrayBase.h" + +namespace WTF { + +class Int32Array : public IntegralTypedArrayBase<int> { +public: + static inline PassRefPtr<Int32Array> create(unsigned length); + static inline PassRefPtr<Int32Array> create(int* array, unsigned length); + static inline PassRefPtr<Int32Array> create(PassRefPtr<ArrayBuffer>, unsigned byteOffset, unsigned length); + + // Can’t use "using" here due to a bug in the RVCT compiler. + bool set(TypedArrayBase<int>* array, unsigned offset) { return TypedArrayBase<int>::set(array, offset); } + void set(unsigned index, double value) { IntegralTypedArrayBase<int>::set(index, value); } + + inline PassRefPtr<Int32Array> subarray(int start) const; + inline PassRefPtr<Int32Array> subarray(int start, int end) const; + +private: + inline Int32Array(PassRefPtr<ArrayBuffer>, + unsigned byteOffset, + unsigned length); + // Make constructor visible to superclass. + friend class TypedArrayBase<int>; + + // Overridden from ArrayBufferView. + virtual bool isIntArray() const { return true; } +}; + +PassRefPtr<Int32Array> Int32Array::create(unsigned length) +{ + return TypedArrayBase<int>::create<Int32Array>(length); +} + +PassRefPtr<Int32Array> Int32Array::create(int* array, unsigned length) +{ + return TypedArrayBase<int>::create<Int32Array>(array, length); +} + +PassRefPtr<Int32Array> Int32Array::create(PassRefPtr<ArrayBuffer> buffer, unsigned byteOffset, unsigned length) +{ + return TypedArrayBase<int>::create<Int32Array>(buffer, byteOffset, length); +} + +Int32Array::Int32Array(PassRefPtr<ArrayBuffer> buffer, unsigned byteOffset, unsigned length) + : IntegralTypedArrayBase<int>(buffer, byteOffset, length) +{ +} + +PassRefPtr<Int32Array> Int32Array::subarray(int start) const +{ + return subarray(start, length()); +} + +PassRefPtr<Int32Array> Int32Array::subarray(int start, int end) const +{ + return subarrayImpl<Int32Array>(start, end); +} + +} // namespace WTF + +using WTF::Int32Array; + +#endif // Int32Array_h diff --git a/Source/JavaScriptCore/wtf/Int8Array.h b/Source/JavaScriptCore/wtf/Int8Array.h new file mode 100644 index 000000000..6db7b6899 --- /dev/null +++ b/Source/JavaScriptCore/wtf/Int8Array.h @@ -0,0 +1,94 @@ +/* + * Copyright (C) 2009 Apple Inc. All rights reserved. + * Copyright (C) 2009 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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 APPLE COMPUTER, INC. ``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 APPLE COMPUTER, INC. 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. + */ + +#ifndef Int8Array_h +#define Int8Array_h + +#include "IntegralTypedArrayBase.h" + +namespace WTF { + +class ArrayBuffer; + +class Int8Array : public IntegralTypedArrayBase<signed char> { +public: + static inline PassRefPtr<Int8Array> create(unsigned length); + static inline PassRefPtr<Int8Array> create(signed char* array, unsigned length); + static inline PassRefPtr<Int8Array> create(PassRefPtr<ArrayBuffer>, unsigned byteOffset, unsigned length); + + // Can’t use "using" here due to a bug in the RVCT compiler. + bool set(TypedArrayBase<signed char>* array, unsigned offset) { return TypedArrayBase<signed char>::set(array, offset); } + void set(unsigned index, double value) { IntegralTypedArrayBase<signed char>::set(index, value); } + + inline PassRefPtr<Int8Array> subarray(int start) const; + inline PassRefPtr<Int8Array> subarray(int start, int end) const; + +private: + inline Int8Array(PassRefPtr<ArrayBuffer>, + unsigned byteOffset, + unsigned length); + // Make constructor visible to superclass. + friend class TypedArrayBase<signed char>; + + // Overridden from ArrayBufferView. + virtual bool isByteArray() const { return true; } +}; + +PassRefPtr<Int8Array> Int8Array::create(unsigned length) +{ + return TypedArrayBase<signed char>::create<Int8Array>(length); +} + +PassRefPtr<Int8Array> Int8Array::create(signed char* array, unsigned length) +{ + return TypedArrayBase<signed char>::create<Int8Array>(array, length); +} + +PassRefPtr<Int8Array> Int8Array::create(PassRefPtr<ArrayBuffer> buffer, unsigned byteOffset, unsigned length) +{ + return TypedArrayBase<signed char>::create<Int8Array>(buffer, byteOffset, length); +} + +Int8Array::Int8Array(PassRefPtr<ArrayBuffer> buffer, unsigned byteOffset, unsigned length) + : IntegralTypedArrayBase<signed char>(buffer, byteOffset, length) +{ +} + +PassRefPtr<Int8Array> Int8Array::subarray(int start) const +{ + return subarray(start, length()); +} + +PassRefPtr<Int8Array> Int8Array::subarray(int start, int end) const +{ + return subarrayImpl<Int8Array>(start, end); +} + +} // namespace WTF + +using WTF::Int8Array; + +#endif // Int8Array_h diff --git a/Source/JavaScriptCore/wtf/IntegralTypedArrayBase.h b/Source/JavaScriptCore/wtf/IntegralTypedArrayBase.h new file mode 100644 index 000000000..48f82a361 --- /dev/null +++ b/Source/JavaScriptCore/wtf/IntegralTypedArrayBase.h @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2010 Apple Inc. All rights reserved. + * Copyright (c) 2010, Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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 APPLE COMPUTER, INC. ``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 APPLE COMPUTER, INC. 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. + */ + +#ifndef IntegralTypedArrayBase_h +#define IntegralTypedArrayBase_h + +#include "TypedArrayBase.h" +#include <limits> +#include <wtf/MathExtras.h> + +// Base class for all WebGL<T>Array types holding integral +// (non-floating-point) values. + +namespace WTF { + +template <typename T> +class IntegralTypedArrayBase : public TypedArrayBase<T> { + public: + void set(unsigned index, double value) + { + if (index >= TypedArrayBase<T>::m_length) + return; + if (isnan(value)) // Clamp NaN to 0 + value = 0; + // The double cast is necessary to get the correct wrapping + // for out-of-range values with Int32Array and Uint32Array. + TypedArrayBase<T>::data()[index] = static_cast<T>(static_cast<int64_t>(value)); + } + + // Invoked by the indexed getter. Does not perform range checks; caller + // is responsible for doing so and returning undefined as necessary. + T item(unsigned index) const + { + ASSERT(index < TypedArrayBase<T>::m_length); + return TypedArrayBase<T>::data()[index]; + } + + protected: + IntegralTypedArrayBase(PassRefPtr<ArrayBuffer> buffer, unsigned byteOffset, unsigned length) + : TypedArrayBase<T>(buffer, byteOffset, length) + { + } +}; + +} // namespace WTF + +using WTF::IntegralTypedArrayBase; + +#endif // IntegralTypedArrayBase_h diff --git a/Source/JavaScriptCore/wtf/ListHashSet.h b/Source/JavaScriptCore/wtf/ListHashSet.h new file mode 100644 index 000000000..3b413406d --- /dev/null +++ b/Source/JavaScriptCore/wtf/ListHashSet.h @@ -0,0 +1,853 @@ +/* + * Copyright (C) 2005, 2006, 2007, 2008, 2011 Apple Inc. All rights reserved. + * Copyright (C) 2011, Benjamin Poulain <ikipou@gmail.com> + * + * This library 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 library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#ifndef WTF_ListHashSet_h +#define WTF_ListHashSet_h + +#include "HashSet.h" +#include "OwnPtr.h" +#include "PassOwnPtr.h" + +namespace WTF { + + // ListHashSet: Just like HashSet, this class provides a Set + // interface - a collection of unique objects with O(1) insertion, + // removal and test for containership. However, it also has an + // order - iterating it will always give back values in the order + // in which they are added. + + // In theory it would be possible to add prepend, insertAfter + // and an append that moves the element to the end even if already present, + // but unclear yet if these are needed. + + template<typename Value, size_t inlineCapacity, typename HashFunctions> class ListHashSet; + + template<typename Value, size_t inlineCapacity, typename HashFunctions> + void deleteAllValues(const ListHashSet<Value, inlineCapacity, HashFunctions>&); + + template<typename ValueArg, size_t inlineCapacity, typename HashArg> class ListHashSetIterator; + template<typename ValueArg, size_t inlineCapacity, typename HashArg> class ListHashSetConstIterator; + template<typename ValueArg, size_t inlineCapacity, typename HashArg> class ListHashSetReverseIterator; + template<typename ValueArg, size_t inlineCapacity, typename HashArg> class ListHashSetConstReverseIterator; + + template<typename ValueArg, size_t inlineCapacity> struct ListHashSetNode; + template<typename ValueArg, size_t inlineCapacity> struct ListHashSetNodeAllocator; + + template<typename HashArg> struct ListHashSetNodeHashFunctions; + template<typename HashArg> struct ListHashSetTranslator; + + template<typename ValueArg, size_t inlineCapacity = 256, typename HashArg = typename DefaultHash<ValueArg>::Hash> class ListHashSet { + WTF_MAKE_FAST_ALLOCATED; + private: + typedef ListHashSetNode<ValueArg, inlineCapacity> Node; + typedef ListHashSetNodeAllocator<ValueArg, inlineCapacity> NodeAllocator; + + typedef HashTraits<Node*> NodeTraits; + typedef ListHashSetNodeHashFunctions<HashArg> NodeHash; + typedef ListHashSetTranslator<HashArg> BaseTranslator; + + typedef HashTable<Node*, Node*, IdentityExtractor, NodeHash, NodeTraits, NodeTraits> ImplType; + typedef HashTableIterator<Node*, Node*, IdentityExtractor, NodeHash, NodeTraits, NodeTraits> ImplTypeIterator; + typedef HashTableConstIterator<Node*, Node*, IdentityExtractor, NodeHash, NodeTraits, NodeTraits> ImplTypeConstIterator; + + typedef HashArg HashFunctions; + + public: + typedef ValueArg ValueType; + + typedef ListHashSetIterator<ValueType, inlineCapacity, HashArg> iterator; + typedef ListHashSetConstIterator<ValueType, inlineCapacity, HashArg> const_iterator; + friend class ListHashSetConstIterator<ValueType, inlineCapacity, HashArg>; + + typedef ListHashSetReverseIterator<ValueType, inlineCapacity, HashArg> reverse_iterator; + typedef ListHashSetConstReverseIterator<ValueType, inlineCapacity, HashArg> const_reverse_iterator; + friend class ListHashSetConstReverseIterator<ValueType, inlineCapacity, HashArg>; + + ListHashSet(); + ListHashSet(const ListHashSet&); + ListHashSet& operator=(const ListHashSet&); + ~ListHashSet(); + + void swap(ListHashSet&); + + int size() const; + int capacity() const; + bool isEmpty() const; + + iterator begin(); + iterator end(); + const_iterator begin() const; + const_iterator end() const; + + reverse_iterator rbegin(); + reverse_iterator rend(); + const_reverse_iterator rbegin() const; + const_reverse_iterator rend() const; + + ValueType& first(); + const ValueType& first() const; + + ValueType& last(); + const ValueType& last() const; + void removeLast(); + + iterator find(const ValueType&); + const_iterator find(const ValueType&) const; + bool contains(const ValueType&) const; + + // An alternate version of find() that finds the object by hashing and comparing + // with some other type, to avoid the cost of type conversion. + // The HashTranslator interface is defined in HashSet. + // FIXME: We should reverse the order of the template arguments so that callers + // can just pass the translator let the compiler deduce T. + template<typename T, typename HashTranslator> iterator find(const T&); + template<typename T, typename HashTranslator> const_iterator find(const T&) const; + template<typename T, typename HashTranslator> bool contains(const T&) const; + + // The return value of add is a pair of an iterator to the new value's location, + // and a bool that is true if an new entry was added. + pair<iterator, bool> add(const ValueType&); + + pair<iterator, bool> insertBefore(const ValueType& beforeValue, const ValueType& newValue); + pair<iterator, bool> insertBefore(iterator it, const ValueType&); + + void remove(const ValueType&); + void remove(iterator); + void clear(); + + private: + void unlinkAndDelete(Node*); + void appendNode(Node*); + void insertNodeBefore(Node* beforeNode, Node* newNode); + void deleteAllNodes(); + + iterator makeIterator(Node*); + const_iterator makeConstIterator(Node*) const; + reverse_iterator makeReverseIterator(Node*); + const_reverse_iterator makeConstReverseIterator(Node*) const; + + friend void deleteAllValues<>(const ListHashSet&); + + ImplType m_impl; + Node* m_head; + Node* m_tail; + OwnPtr<NodeAllocator> m_allocator; + }; + + template<typename ValueArg, size_t inlineCapacity> struct ListHashSetNodeAllocator { + typedef ListHashSetNode<ValueArg, inlineCapacity> Node; + typedef ListHashSetNodeAllocator<ValueArg, inlineCapacity> NodeAllocator; + + ListHashSetNodeAllocator() + : m_freeList(pool()) + , m_isDoneWithInitialFreeList(false) + { + memset(m_pool.pool, 0, sizeof(m_pool.pool)); + } + + Node* allocate() + { + Node* result = m_freeList; + + if (!result) + return static_cast<Node*>(fastMalloc(sizeof(Node))); + + ASSERT(!result->m_isAllocated); + + Node* next = result->m_next; + ASSERT(!next || !next->m_isAllocated); + if (!next && !m_isDoneWithInitialFreeList) { + next = result + 1; + if (next == pastPool()) { + m_isDoneWithInitialFreeList = true; + next = 0; + } else { + ASSERT(inPool(next)); + ASSERT(!next->m_isAllocated); + } + } + m_freeList = next; + + return result; + } + + void deallocate(Node* node) + { + if (inPool(node)) { +#ifndef NDEBUG + node->m_isAllocated = false; +#endif + node->m_next = m_freeList; + m_freeList = node; + return; + } + + fastFree(node); + } + + private: + Node* pool() { return reinterpret_cast_ptr<Node*>(m_pool.pool); } + Node* pastPool() { return pool() + m_poolSize; } + + bool inPool(Node* node) + { + return node >= pool() && node < pastPool(); + } + + Node* m_freeList; + bool m_isDoneWithInitialFreeList; + static const size_t m_poolSize = inlineCapacity; + union { + char pool[sizeof(Node) * m_poolSize]; + double forAlignment; + } m_pool; + }; + + template<typename ValueArg, size_t inlineCapacity> struct ListHashSetNode { + typedef ListHashSetNodeAllocator<ValueArg, inlineCapacity> NodeAllocator; + + ListHashSetNode(ValueArg value) + : m_value(value) + , m_prev(0) + , m_next(0) +#ifndef NDEBUG + , m_isAllocated(true) +#endif + { + } + + void* operator new(size_t, NodeAllocator* allocator) + { + return allocator->allocate(); + } + void destroy(NodeAllocator* allocator) + { + this->~ListHashSetNode(); + allocator->deallocate(this); + } + + ValueArg m_value; + ListHashSetNode* m_prev; + ListHashSetNode* m_next; + +#ifndef NDEBUG + bool m_isAllocated; +#endif + }; + + template<typename HashArg> struct ListHashSetNodeHashFunctions { + template<typename T> static unsigned hash(const T& key) { return HashArg::hash(key->m_value); } + template<typename T> static bool equal(const T& a, const T& b) { return HashArg::equal(a->m_value, b->m_value); } + static const bool safeToCompareToEmptyOrDeleted = false; + }; + + template<typename ValueArg, size_t inlineCapacity, typename HashArg> class ListHashSetIterator { + private: + typedef ListHashSet<ValueArg, inlineCapacity, HashArg> ListHashSetType; + typedef ListHashSetIterator<ValueArg, inlineCapacity, HashArg> iterator; + typedef ListHashSetConstIterator<ValueArg, inlineCapacity, HashArg> const_iterator; + typedef ListHashSetNode<ValueArg, inlineCapacity> Node; + typedef ValueArg ValueType; + typedef ValueType& ReferenceType; + typedef ValueType* PointerType; + + friend class ListHashSet<ValueArg, inlineCapacity, HashArg>; + + ListHashSetIterator(const ListHashSetType* set, Node* position) : m_iterator(set, position) { } + + public: + ListHashSetIterator() { } + + // default copy, assignment and destructor are OK + + PointerType get() const { return const_cast<PointerType>(m_iterator.get()); } + ReferenceType operator*() const { return *get(); } + PointerType operator->() const { return get(); } + + iterator& operator++() { ++m_iterator; return *this; } + + // postfix ++ intentionally omitted + + iterator& operator--() { --m_iterator; return *this; } + + // postfix -- intentionally omitted + + // Comparison. + bool operator==(const iterator& other) const { return m_iterator == other.m_iterator; } + bool operator!=(const iterator& other) const { return m_iterator != other.m_iterator; } + + operator const_iterator() const { return m_iterator; } + + private: + Node* node() { return m_iterator.node(); } + + const_iterator m_iterator; + }; + + template<typename ValueArg, size_t inlineCapacity, typename HashArg> class ListHashSetConstIterator { + private: + typedef ListHashSet<ValueArg, inlineCapacity, HashArg> ListHashSetType; + typedef ListHashSetIterator<ValueArg, inlineCapacity, HashArg> iterator; + typedef ListHashSetConstIterator<ValueArg, inlineCapacity, HashArg> const_iterator; + typedef ListHashSetNode<ValueArg, inlineCapacity> Node; + typedef ValueArg ValueType; + typedef const ValueType& ReferenceType; + typedef const ValueType* PointerType; + + friend class ListHashSet<ValueArg, inlineCapacity, HashArg>; + friend class ListHashSetIterator<ValueArg, inlineCapacity, HashArg>; + + ListHashSetConstIterator(const ListHashSetType* set, Node* position) + : m_set(set) + , m_position(position) + { + } + + public: + ListHashSetConstIterator() + { + } + + PointerType get() const + { + return &m_position->m_value; + } + ReferenceType operator*() const { return *get(); } + PointerType operator->() const { return get(); } + + const_iterator& operator++() + { + ASSERT(m_position != 0); + m_position = m_position->m_next; + return *this; + } + + // postfix ++ intentionally omitted + + const_iterator& operator--() + { + ASSERT(m_position != m_set->m_head); + if (!m_position) + m_position = m_set->m_tail; + else + m_position = m_position->m_prev; + return *this; + } + + // postfix -- intentionally omitted + + // Comparison. + bool operator==(const const_iterator& other) const + { + return m_position == other.m_position; + } + bool operator!=(const const_iterator& other) const + { + return m_position != other.m_position; + } + + private: + Node* node() { return m_position; } + + const ListHashSetType* m_set; + Node* m_position; + }; + + template<typename ValueArg, size_t inlineCapacity, typename HashArg> class ListHashSetReverseIterator { + private: + typedef ListHashSet<ValueArg, inlineCapacity, HashArg> ListHashSetType; + typedef ListHashSetReverseIterator<ValueArg, inlineCapacity, HashArg> reverse_iterator; + typedef ListHashSetConstIterator<ValueArg, inlineCapacity, HashArg> const_reverse_iterator; + typedef ListHashSetNode<ValueArg, inlineCapacity> Node; + typedef ValueArg ValueType; + typedef ValueType& ReferenceType; + typedef ValueType* PointerType; + + friend class ListHashSet<ValueArg, inlineCapacity, HashArg>; + + ListHashSetReverseIterator(const ListHashSetType* set, Node* position) : m_iterator(set, position) { } + + public: + ListHashSetReverseIterator() { } + + // default copy, assignment and destructor are OK + + PointerType get() const { return const_cast<PointerType>(m_iterator.get()); } + ReferenceType operator*() const { return *get(); } + PointerType operator->() const { return get(); } + + reverse_iterator& operator++() { ++m_iterator; return *this; } + + // postfix ++ intentionally omitted + + reverse_iterator& operator--() { --m_iterator; return *this; } + + // postfix -- intentionally omitted + + // Comparison. + bool operator==(const reverse_iterator& other) const { return m_iterator == other.m_iterator; } + bool operator!=(const reverse_iterator& other) const { return m_iterator != other.m_iterator; } + + operator const_reverse_iterator() const { return m_iterator; } + + private: + Node* node() { return m_iterator.node(); } + + const_reverse_iterator m_iterator; + }; + + template<typename ValueArg, size_t inlineCapacity, typename HashArg> class ListHashSetConstReverseIterator { + private: + typedef ListHashSet<ValueArg, inlineCapacity, HashArg> ListHashSetType; + typedef ListHashSetReverseIterator<ValueArg, inlineCapacity, HashArg> reverse_iterator; + typedef ListHashSetConstReverseIterator<ValueArg, inlineCapacity, HashArg> const_reverse_iterator; + typedef ListHashSetNode<ValueArg, inlineCapacity> Node; + typedef ValueArg ValueType; + typedef const ValueType& ReferenceType; + typedef const ValueType* PointerType; + + friend class ListHashSet<ValueArg, inlineCapacity, HashArg>; + friend class ListHashSetReverseIterator<ValueArg, inlineCapacity, HashArg>; + + ListHashSetConstReverseIterator(const ListHashSetType* set, Node* position) + : m_set(set) + , m_position(position) + { + } + + public: + ListHashSetConstReverseIterator() + { + } + + PointerType get() const + { + return &m_position->m_value; + } + ReferenceType operator*() const { return *get(); } + PointerType operator->() const { return get(); } + + const_reverse_iterator& operator++() + { + ASSERT(m_position != 0); + m_position = m_position->m_prev; + return *this; + } + + // postfix ++ intentionally omitted + + const_reverse_iterator& operator--() + { + ASSERT(m_position != m_set->m_tail); + if (!m_position) + m_position = m_set->m_head; + else + m_position = m_position->m_next; + return *this; + } + + // postfix -- intentionally omitted + + // Comparison. + bool operator==(const const_reverse_iterator& other) const + { + return m_position == other.m_position; + } + bool operator!=(const const_reverse_iterator& other) const + { + return m_position != other.m_position; + } + + private: + Node* node() { return m_position; } + + const ListHashSetType* m_set; + Node* m_position; + }; + + template<typename HashFunctions> + struct ListHashSetTranslator { + template<typename T> static unsigned hash(const T& key) { return HashFunctions::hash(key); } + template<typename T, typename U> static bool equal(const T& a, const U& b) { return HashFunctions::equal(a->m_value, b); } + template<typename T, typename U, typename V> static void translate(T*& location, const U& key, const V& allocator) + { + location = new (allocator) T(key); + } + }; + + template<typename T, size_t inlineCapacity, typename U> + inline ListHashSet<T, inlineCapacity, U>::ListHashSet() + : m_head(0) + , m_tail(0) + , m_allocator(adoptPtr(new NodeAllocator)) + { + } + + template<typename T, size_t inlineCapacity, typename U> + inline ListHashSet<T, inlineCapacity, U>::ListHashSet(const ListHashSet& other) + : m_head(0) + , m_tail(0) + , m_allocator(adoptPtr(new NodeAllocator)) + { + const_iterator end = other.end(); + for (const_iterator it = other.begin(); it != end; ++it) + add(*it); + } + + template<typename T, size_t inlineCapacity, typename U> + inline ListHashSet<T, inlineCapacity, U>& ListHashSet<T, inlineCapacity, U>::operator=(const ListHashSet& other) + { + ListHashSet tmp(other); + swap(tmp); + return *this; + } + + template<typename T, size_t inlineCapacity, typename U> + inline void ListHashSet<T, inlineCapacity, U>::swap(ListHashSet& other) + { + m_impl.swap(other.m_impl); + std::swap(m_head, other.m_head); + std::swap(m_tail, other.m_tail); + m_allocator.swap(other.m_allocator); + } + + template<typename T, size_t inlineCapacity, typename U> + inline ListHashSet<T, inlineCapacity, U>::~ListHashSet() + { + deleteAllNodes(); + } + + template<typename T, size_t inlineCapacity, typename U> + inline int ListHashSet<T, inlineCapacity, U>::size() const + { + return m_impl.size(); + } + + template<typename T, size_t inlineCapacity, typename U> + inline int ListHashSet<T, inlineCapacity, U>::capacity() const + { + return m_impl.capacity(); + } + + template<typename T, size_t inlineCapacity, typename U> + inline bool ListHashSet<T, inlineCapacity, U>::isEmpty() const + { + return m_impl.isEmpty(); + } + + template<typename T, size_t inlineCapacity, typename U> + inline typename ListHashSet<T, inlineCapacity, U>::iterator ListHashSet<T, inlineCapacity, U>::begin() + { + return makeIterator(m_head); + } + + template<typename T, size_t inlineCapacity, typename U> + inline typename ListHashSet<T, inlineCapacity, U>::iterator ListHashSet<T, inlineCapacity, U>::end() + { + return makeIterator(0); + } + + template<typename T, size_t inlineCapacity, typename U> + inline typename ListHashSet<T, inlineCapacity, U>::const_iterator ListHashSet<T, inlineCapacity, U>::begin() const + { + return makeConstIterator(m_head); + } + + template<typename T, size_t inlineCapacity, typename U> + inline typename ListHashSet<T, inlineCapacity, U>::const_iterator ListHashSet<T, inlineCapacity, U>::end() const + { + return makeConstIterator(0); + } + + template<typename T, size_t inlineCapacity, typename U> + inline typename ListHashSet<T, inlineCapacity, U>::reverse_iterator ListHashSet<T, inlineCapacity, U>::rbegin() + { + return makeReverseIterator(m_tail); + } + + template<typename T, size_t inlineCapacity, typename U> + inline typename ListHashSet<T, inlineCapacity, U>::reverse_iterator ListHashSet<T, inlineCapacity, U>::rend() + { + return makeReverseIterator(0); + } + + template<typename T, size_t inlineCapacity, typename U> + inline typename ListHashSet<T, inlineCapacity, U>::const_reverse_iterator ListHashSet<T, inlineCapacity, U>::rbegin() const + { + return makeConstReverseIterator(m_tail); + } + + template<typename T, size_t inlineCapacity, typename U> + inline typename ListHashSet<T, inlineCapacity, U>::const_reverse_iterator ListHashSet<T, inlineCapacity, U>::rend() const + { + return makeConstReverseIterator(0); + } + + template<typename T, size_t inlineCapacity, typename U> + inline T& ListHashSet<T, inlineCapacity, U>::first() + { + ASSERT(!isEmpty()); + return m_head->m_value; + } + + template<typename T, size_t inlineCapacity, typename U> + inline const T& ListHashSet<T, inlineCapacity, U>::first() const + { + ASSERT(!isEmpty()); + return m_head->m_value; + } + + template<typename T, size_t inlineCapacity, typename U> + inline T& ListHashSet<T, inlineCapacity, U>::last() + { + ASSERT(!isEmpty()); + return m_tail->m_value; + } + + template<typename T, size_t inlineCapacity, typename U> + inline const T& ListHashSet<T, inlineCapacity, U>::last() const + { + ASSERT(!isEmpty()); + return m_tail->m_value; + } + + template<typename T, size_t inlineCapacity, typename U> + inline void ListHashSet<T, inlineCapacity, U>::removeLast() + { + ASSERT(!isEmpty()); + m_impl.remove(m_tail); + unlinkAndDelete(m_tail); + } + + template<typename T, size_t inlineCapacity, typename U> + inline typename ListHashSet<T, inlineCapacity, U>::iterator ListHashSet<T, inlineCapacity, U>::find(const ValueType& value) + { + ImplTypeIterator it = m_impl.template find<BaseTranslator>(value); + if (it == m_impl.end()) + return end(); + return makeIterator(*it); + } + + template<typename T, size_t inlineCapacity, typename U> + inline typename ListHashSet<T, inlineCapacity, U>::const_iterator ListHashSet<T, inlineCapacity, U>::find(const ValueType& value) const + { + ImplTypeConstIterator it = m_impl.template find<BaseTranslator>(value); + if (it == m_impl.end()) + return end(); + return makeConstIterator(*it); + } + + template<typename Translator> + struct ListHashSetTranslatorAdapter { + template<typename T> static unsigned hash(const T& key) { return Translator::hash(key); } + template<typename T, typename U> static bool equal(const T& a, const U& b) { return Translator::equal(a->m_value, b); } + }; + + template<typename ValueType, size_t inlineCapacity, typename U> + template<typename T, typename HashTranslator> + inline typename ListHashSet<ValueType, inlineCapacity, U>::iterator ListHashSet<ValueType, inlineCapacity, U>::find(const T& value) + { + ImplTypeConstIterator it = m_impl.template find<ListHashSetTranslatorAdapter<HashTranslator> >(value); + if (it == m_impl.end()) + return end(); + return makeIterator(*it); + } + + template<typename ValueType, size_t inlineCapacity, typename U> + template<typename T, typename HashTranslator> + inline typename ListHashSet<ValueType, inlineCapacity, U>::const_iterator ListHashSet<ValueType, inlineCapacity, U>::find(const T& value) const + { + ImplTypeConstIterator it = m_impl.template find<ListHashSetTranslatorAdapter<HashTranslator> >(value); + if (it == m_impl.end()) + return end(); + return makeConstIterator(*it); + } + + template<typename ValueType, size_t inlineCapacity, typename U> + template<typename T, typename HashTranslator> + inline bool ListHashSet<ValueType, inlineCapacity, U>::contains(const T& value) const + { + return m_impl.template contains<ListHashSetTranslatorAdapter<HashTranslator> >(value); + } + + template<typename T, size_t inlineCapacity, typename U> + inline bool ListHashSet<T, inlineCapacity, U>::contains(const ValueType& value) const + { + return m_impl.template contains<BaseTranslator>(value); + } + + template<typename T, size_t inlineCapacity, typename U> + pair<typename ListHashSet<T, inlineCapacity, U>::iterator, bool> ListHashSet<T, inlineCapacity, U>::add(const ValueType &value) + { + pair<typename ImplType::iterator, bool> result = m_impl.template add<BaseTranslator>(value, m_allocator.get()); + if (result.second) + appendNode(*result.first); + return std::make_pair(makeIterator(*result.first), result.second); + } + + template<typename T, size_t inlineCapacity, typename U> + pair<typename ListHashSet<T, inlineCapacity, U>::iterator, bool> ListHashSet<T, inlineCapacity, U>::insertBefore(iterator it, const ValueType& newValue) + { + pair<typename ImplType::iterator, bool> result = m_impl.template add<BaseTranslator>(newValue, m_allocator.get()); + if (result.second) + insertNodeBefore(it.node(), *result.first); + return std::make_pair(makeIterator(*result.first), result.second); + + } + + template<typename T, size_t inlineCapacity, typename U> + pair<typename ListHashSet<T, inlineCapacity, U>::iterator, bool> ListHashSet<T, inlineCapacity, U>::insertBefore(const ValueType& beforeValue, const ValueType& newValue) + { + return insertBefore(find(beforeValue), newValue); + } + + template<typename T, size_t inlineCapacity, typename U> + inline void ListHashSet<T, inlineCapacity, U>::remove(iterator it) + { + if (it == end()) + return; + m_impl.remove(it.node()); + unlinkAndDelete(it.node()); + } + + template<typename T, size_t inlineCapacity, typename U> + inline void ListHashSet<T, inlineCapacity, U>::remove(const ValueType& value) + { + remove(find(value)); + } + + template<typename T, size_t inlineCapacity, typename U> + inline void ListHashSet<T, inlineCapacity, U>::clear() + { + deleteAllNodes(); + m_impl.clear(); + m_head = 0; + m_tail = 0; + } + + template<typename T, size_t inlineCapacity, typename U> + void ListHashSet<T, inlineCapacity, U>::unlinkAndDelete(Node* node) + { + if (!node->m_prev) { + ASSERT(node == m_head); + m_head = node->m_next; + } else { + ASSERT(node != m_head); + node->m_prev->m_next = node->m_next; + } + + if (!node->m_next) { + ASSERT(node == m_tail); + m_tail = node->m_prev; + } else { + ASSERT(node != m_tail); + node->m_next->m_prev = node->m_prev; + } + + node->destroy(m_allocator.get()); + } + + template<typename T, size_t inlineCapacity, typename U> + void ListHashSet<T, inlineCapacity, U>::appendNode(Node* node) + { + node->m_prev = m_tail; + node->m_next = 0; + + if (m_tail) { + ASSERT(m_head); + m_tail->m_next = node; + } else { + ASSERT(!m_head); + m_head = node; + } + + m_tail = node; + } + + template<typename T, size_t inlineCapacity, typename U> + void ListHashSet<T, inlineCapacity, U>::insertNodeBefore(Node* beforeNode, Node* newNode) + { + if (!beforeNode) + return appendNode(newNode); + + newNode->m_next = beforeNode; + newNode->m_prev = beforeNode->m_prev; + if (beforeNode->m_prev) + beforeNode->m_prev->m_next = newNode; + beforeNode->m_prev = newNode; + + if (!newNode->m_prev) + m_head = newNode; + } + + template<typename T, size_t inlineCapacity, typename U> + void ListHashSet<T, inlineCapacity, U>::deleteAllNodes() + { + if (!m_head) + return; + + for (Node* node = m_head, *next = m_head->m_next; node; node = next, next = node ? node->m_next : 0) + node->destroy(m_allocator.get()); + } + + template<typename T, size_t inlineCapacity, typename U> + inline ListHashSetReverseIterator<T, inlineCapacity, U> ListHashSet<T, inlineCapacity, U>::makeReverseIterator(Node* position) + { + return ListHashSetReverseIterator<T, inlineCapacity, U>(this, position); + } + + template<typename T, size_t inlineCapacity, typename U> + inline ListHashSetConstReverseIterator<T, inlineCapacity, U> ListHashSet<T, inlineCapacity, U>::makeConstReverseIterator(Node* position) const + { + return ListHashSetConstReverseIterator<T, inlineCapacity, U>(this, position); + } + + template<typename T, size_t inlineCapacity, typename U> + inline ListHashSetIterator<T, inlineCapacity, U> ListHashSet<T, inlineCapacity, U>::makeIterator(Node* position) + { + return ListHashSetIterator<T, inlineCapacity, U>(this, position); + } + + template<typename T, size_t inlineCapacity, typename U> + inline ListHashSetConstIterator<T, inlineCapacity, U> ListHashSet<T, inlineCapacity, U>::makeConstIterator(Node* position) const + { + return ListHashSetConstIterator<T, inlineCapacity, U>(this, position); + } + template<bool, typename ValueType, typename HashTableType> + void deleteAllValues(HashTableType& collection) + { + typedef typename HashTableType::const_iterator iterator; + iterator end = collection.end(); + for (iterator it = collection.begin(); it != end; ++it) + delete (*it)->m_value; + } + + template<typename T, size_t inlineCapacity, typename U> + inline void deleteAllValues(const ListHashSet<T, inlineCapacity, U>& collection) + { + deleteAllValues<true, typename ListHashSet<T, inlineCapacity, U>::ValueType>(collection.m_impl); + } + +} // namespace WTF + +using WTF::ListHashSet; + +#endif /* WTF_ListHashSet_h */ diff --git a/Source/JavaScriptCore/wtf/ListRefPtr.h b/Source/JavaScriptCore/wtf/ListRefPtr.h new file mode 100644 index 000000000..4ba0632d7 --- /dev/null +++ b/Source/JavaScriptCore/wtf/ListRefPtr.h @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2005, 2006, 2008 Apple Inc. All rights reserved. + * + * This library 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 library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#ifndef WTF_ListRefPtr_h +#define WTF_ListRefPtr_h + +#include <wtf/RefPtr.h> + +namespace WTF { + + // Specialized version of RefPtr desgined for use in singly-linked lists. + // Derefs the list iteratively to avoid recursive derefing that can overflow the stack. + template <typename T> class ListRefPtr : public RefPtr<T> { + public: + ListRefPtr() : RefPtr<T>() {} + ListRefPtr(T* ptr) : RefPtr<T>(ptr) {} + ListRefPtr(const RefPtr<T>& o) : RefPtr<T>(o) {} + // see comment in PassRefPtr.h for why this takes const reference + template <typename U> ListRefPtr(const PassRefPtr<U>& o) : RefPtr<T>(o) {} + + ~ListRefPtr() + { + RefPtr<T> reaper = this->release(); + while (reaper && reaper->hasOneRef()) + reaper = reaper->releaseNext(); // implicitly protects reaper->next, then derefs reaper + } + + ListRefPtr& operator=(T* optr) { RefPtr<T>::operator=(optr); return *this; } + ListRefPtr& operator=(const RefPtr<T>& o) { RefPtr<T>::operator=(o); return *this; } + ListRefPtr& operator=(const PassRefPtr<T>& o) { RefPtr<T>::operator=(o); return *this; } + template <typename U> ListRefPtr& operator=(const RefPtr<U>& o) { RefPtr<T>::operator=(o); return *this; } + template <typename U> ListRefPtr& operator=(const PassRefPtr<U>& o) { RefPtr<T>::operator=(o); return *this; } + }; + + template <typename T> inline T* getPtr(const ListRefPtr<T>& p) + { + return p.get(); + } + +} // namespace WTF + +using WTF::ListRefPtr; + +#endif // WTF_ListRefPtr_h diff --git a/Source/JavaScriptCore/wtf/Locker.h b/Source/JavaScriptCore/wtf/Locker.h new file mode 100644 index 000000000..c465b99ea --- /dev/null +++ b/Source/JavaScriptCore/wtf/Locker.h @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2008 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of Apple Computer, Inc. ("Apple") 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 APPLE AND ITS 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 APPLE OR ITS 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. + */ +#ifndef Locker_h +#define Locker_h + +#include <wtf/Noncopyable.h> + +namespace WTF { + +template <typename T> class Locker { + WTF_MAKE_NONCOPYABLE(Locker); +public: + Locker(T& lockable) : m_lockable(lockable) { m_lockable.lock(); } + ~Locker() { m_lockable.unlock(); } +private: + T& m_lockable; +}; + +} + +using WTF::Locker; + +#endif diff --git a/Source/JavaScriptCore/wtf/MD5.cpp b/Source/JavaScriptCore/wtf/MD5.cpp new file mode 100644 index 000000000..07bbadd9f --- /dev/null +++ b/Source/JavaScriptCore/wtf/MD5.cpp @@ -0,0 +1,309 @@ +// The original file was copied from sqlite, and was in the public domain. +// Modifications Copyright 2006 Google Inc. All Rights Reserved +/* + * Copyright (C) 2010 Google Inc. 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 Google Inc. 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. + */ +/* + * This code implements the MD5 message-digest algorithm. + * The algorithm is due to Ron Rivest. This code was + * written by Colin Plumb in 1993, no copyright is claimed. + * This code is in the public domain; do with it what you wish. + * + * Equivalent code is available from RSA Data Security, Inc. + * This code has been tested against that, and is equivalent, + * except that you don't need to include two pages of legalese + * with every copy. + * + * To compute the message digest of a chunk of bytes, construct an + * MD5 instance, call addBytes as needed on buffers full of bytes, + * and then call checksum, which will fill a supplied 16-byte array + * with the digest. + */ + +#include "config.h" +#include "MD5.h" + +#include "Assertions.h" +#ifndef NDEBUG +#include "StringExtras.h" +#include "text/CString.h" +#endif +#include <wtf/StdLibExtras.h> + +namespace WTF { + +#ifdef NDEBUG +static inline void testMD5() { } +#else +// MD5 test case. +static bool isTestMD5Done; + +static void expectMD5(CString input, CString expected) +{ + MD5 md5; + md5.addBytes(reinterpret_cast<const uint8_t*>(input.data()), input.length()); + Vector<uint8_t, 16> digest; + md5.checksum(digest); + char* buf = 0; + CString actual = CString::newUninitialized(32, buf); + for (size_t i = 0; i < 16; i++) { + snprintf(buf, 3, "%02x", digest.at(i)); + buf += 2; + } + ASSERT_WITH_MESSAGE(actual == expected, "input:%s[%lu] actual:%s expected:%s", input.data(), static_cast<unsigned long>(input.length()), actual.data(), expected.data()); +} + +static void testMD5() +{ + if (isTestMD5Done) + return; + isTestMD5Done = true; + + // MD5 Test suite from http://www.ietf.org/rfc/rfc1321.txt + expectMD5("", "d41d8cd98f00b204e9800998ecf8427e"); + expectMD5("a", "0cc175b9c0f1b6a831c399e269772661"); + expectMD5("abc", "900150983cd24fb0d6963f7d28e17f72"); + expectMD5("message digest", "f96b697d7cb7938d525a2f31aaf161d0"); + expectMD5("abcdefghijklmnopqrstuvwxyz", "c3fcd3d76192e4007dfb496cca67e13b"); + expectMD5("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789", "d174ab98d277d9f5a5611c2c9f419d9f"); + expectMD5("12345678901234567890123456789012345678901234567890123456789012345678901234567890", "57edf4a22be3c955ac49da2e2107b67a"); +} +#endif + +// Note: this code is harmless on little-endian machines. + +static void reverseBytes(uint8_t* buf, unsigned longs) +{ + ASSERT(longs > 0); + do { + uint32_t t = static_cast<uint32_t>(buf[3] << 8 | buf[2]) << 16 | buf[1] << 8 | buf[0]; + ASSERT_WITH_MESSAGE(!(reinterpret_cast<uintptr_t>(buf) % sizeof(t)), "alignment error of buf"); + *reinterpret_cast_ptr<uint32_t *>(buf) = t; + buf += 4; + } while (--longs); +} + +// The four core functions. +// F1 is originally defined as (x & y | ~x & z), but optimized somewhat: 4 bit ops -> 3 bit ops. +#define F1(x, y, z) (z ^ (x & (y ^ z))) +#define F2(x, y, z) F1(z, x, y) +#define F3(x, y, z) (x ^ y ^ z) +#define F4(x, y, z) (y ^ (x | ~z)) + +// This is the central step in the MD5 algorithm. +#define MD5STEP(f, w, x, y, z, data, s) \ + (w += f(x, y, z) + data, w = w << s | w >> (32 - s), w += x) + +static void MD5Transform(uint32_t buf[4], const uint32_t in[16]) +{ + uint32_t a = buf[0]; + uint32_t b = buf[1]; + uint32_t c = buf[2]; + uint32_t d = buf[3]; + + MD5STEP(F1, a, b, c, d, in[ 0]+0xd76aa478, 7); + MD5STEP(F1, d, a, b, c, in[ 1]+0xe8c7b756, 12); + MD5STEP(F1, c, d, a, b, in[ 2]+0x242070db, 17); + MD5STEP(F1, b, c, d, a, in[ 3]+0xc1bdceee, 22); + MD5STEP(F1, a, b, c, d, in[ 4]+0xf57c0faf, 7); + MD5STEP(F1, d, a, b, c, in[ 5]+0x4787c62a, 12); + MD5STEP(F1, c, d, a, b, in[ 6]+0xa8304613, 17); + MD5STEP(F1, b, c, d, a, in[ 7]+0xfd469501, 22); + MD5STEP(F1, a, b, c, d, in[ 8]+0x698098d8, 7); + MD5STEP(F1, d, a, b, c, in[ 9]+0x8b44f7af, 12); + MD5STEP(F1, c, d, a, b, in[10]+0xffff5bb1, 17); + MD5STEP(F1, b, c, d, a, in[11]+0x895cd7be, 22); + MD5STEP(F1, a, b, c, d, in[12]+0x6b901122, 7); + MD5STEP(F1, d, a, b, c, in[13]+0xfd987193, 12); + MD5STEP(F1, c, d, a, b, in[14]+0xa679438e, 17); + MD5STEP(F1, b, c, d, a, in[15]+0x49b40821, 22); + + MD5STEP(F2, a, b, c, d, in[ 1]+0xf61e2562, 5); + MD5STEP(F2, d, a, b, c, in[ 6]+0xc040b340, 9); + MD5STEP(F2, c, d, a, b, in[11]+0x265e5a51, 14); + MD5STEP(F2, b, c, d, a, in[ 0]+0xe9b6c7aa, 20); + MD5STEP(F2, a, b, c, d, in[ 5]+0xd62f105d, 5); + MD5STEP(F2, d, a, b, c, in[10]+0x02441453, 9); + MD5STEP(F2, c, d, a, b, in[15]+0xd8a1e681, 14); + MD5STEP(F2, b, c, d, a, in[ 4]+0xe7d3fbc8, 20); + MD5STEP(F2, a, b, c, d, in[ 9]+0x21e1cde6, 5); + MD5STEP(F2, d, a, b, c, in[14]+0xc33707d6, 9); + MD5STEP(F2, c, d, a, b, in[ 3]+0xf4d50d87, 14); + MD5STEP(F2, b, c, d, a, in[ 8]+0x455a14ed, 20); + MD5STEP(F2, a, b, c, d, in[13]+0xa9e3e905, 5); + MD5STEP(F2, d, a, b, c, in[ 2]+0xfcefa3f8, 9); + MD5STEP(F2, c, d, a, b, in[ 7]+0x676f02d9, 14); + MD5STEP(F2, b, c, d, a, in[12]+0x8d2a4c8a, 20); + + MD5STEP(F3, a, b, c, d, in[ 5]+0xfffa3942, 4); + MD5STEP(F3, d, a, b, c, in[ 8]+0x8771f681, 11); + MD5STEP(F3, c, d, a, b, in[11]+0x6d9d6122, 16); + MD5STEP(F3, b, c, d, a, in[14]+0xfde5380c, 23); + MD5STEP(F3, a, b, c, d, in[ 1]+0xa4beea44, 4); + MD5STEP(F3, d, a, b, c, in[ 4]+0x4bdecfa9, 11); + MD5STEP(F3, c, d, a, b, in[ 7]+0xf6bb4b60, 16); + MD5STEP(F3, b, c, d, a, in[10]+0xbebfbc70, 23); + MD5STEP(F3, a, b, c, d, in[13]+0x289b7ec6, 4); + MD5STEP(F3, d, a, b, c, in[ 0]+0xeaa127fa, 11); + MD5STEP(F3, c, d, a, b, in[ 3]+0xd4ef3085, 16); + MD5STEP(F3, b, c, d, a, in[ 6]+0x04881d05, 23); + MD5STEP(F3, a, b, c, d, in[ 9]+0xd9d4d039, 4); + MD5STEP(F3, d, a, b, c, in[12]+0xe6db99e5, 11); + MD5STEP(F3, c, d, a, b, in[15]+0x1fa27cf8, 16); + MD5STEP(F3, b, c, d, a, in[ 2]+0xc4ac5665, 23); + + MD5STEP(F4, a, b, c, d, in[ 0]+0xf4292244, 6); + MD5STEP(F4, d, a, b, c, in[ 7]+0x432aff97, 10); + MD5STEP(F4, c, d, a, b, in[14]+0xab9423a7, 15); + MD5STEP(F4, b, c, d, a, in[ 5]+0xfc93a039, 21); + MD5STEP(F4, a, b, c, d, in[12]+0x655b59c3, 6); + MD5STEP(F4, d, a, b, c, in[ 3]+0x8f0ccc92, 10); + MD5STEP(F4, c, d, a, b, in[10]+0xffeff47d, 15); + MD5STEP(F4, b, c, d, a, in[ 1]+0x85845dd1, 21); + MD5STEP(F4, a, b, c, d, in[ 8]+0x6fa87e4f, 6); + MD5STEP(F4, d, a, b, c, in[15]+0xfe2ce6e0, 10); + MD5STEP(F4, c, d, a, b, in[ 6]+0xa3014314, 15); + MD5STEP(F4, b, c, d, a, in[13]+0x4e0811a1, 21); + MD5STEP(F4, a, b, c, d, in[ 4]+0xf7537e82, 6); + MD5STEP(F4, d, a, b, c, in[11]+0xbd3af235, 10); + MD5STEP(F4, c, d, a, b, in[ 2]+0x2ad7d2bb, 15); + MD5STEP(F4, b, c, d, a, in[ 9]+0xeb86d391, 21); + + buf[0] += a; + buf[1] += b; + buf[2] += c; + buf[3] += d; +} + +MD5::MD5() +{ + // FIXME: Move unit tests somewhere outside the constructor. See bug 55853. + testMD5(); + m_buf[0] = 0x67452301; + m_buf[1] = 0xefcdab89; + m_buf[2] = 0x98badcfe; + m_buf[3] = 0x10325476; + m_bits[0] = 0; + m_bits[1] = 0; + memset(m_in, 0, sizeof(m_in)); + ASSERT_WITH_MESSAGE(!(reinterpret_cast<uintptr_t>(m_in) % sizeof(uint32_t)), "alignment error of m_in"); +} + +void MD5::addBytes(const uint8_t* input, size_t length) +{ + const uint8_t* buf = input; + + // Update bitcount + uint32_t t = m_bits[0]; + m_bits[0] = t + (length << 3); + if (m_bits[0] < t) + m_bits[1]++; // Carry from low to high + m_bits[1] += length >> 29; + + t = (t >> 3) & 0x3f; // Bytes already in shsInfo->data + + // Handle any leading odd-sized chunks + + if (t) { + uint8_t* p = m_in + t; + + t = 64 - t; + if (length < t) { + memcpy(p, buf, length); + return; + } + memcpy(p, buf, t); + reverseBytes(m_in, 16); + MD5Transform(m_buf, reinterpret_cast_ptr<uint32_t*>(m_in)); // m_in is 4-byte aligned. + buf += t; + length -= t; + } + + // Process data in 64-byte chunks + + while (length >= 64) { + memcpy(m_in, buf, 64); + reverseBytes(m_in, 16); + MD5Transform(m_buf, reinterpret_cast_ptr<uint32_t*>(m_in)); // m_in is 4-byte aligned. + buf += 64; + length -= 64; + } + + // Handle any remaining bytes of data. + memcpy(m_in, buf, length); +} + +void MD5::checksum(Vector<uint8_t, 16>& digest) +{ + // Compute number of bytes mod 64 + unsigned count = (m_bits[0] >> 3) & 0x3F; + + // Set the first char of padding to 0x80. This is safe since there is + // always at least one byte free + uint8_t* p = m_in + count; + *p++ = 0x80; + + // Bytes of padding needed to make 64 bytes + count = 64 - 1 - count; + + // Pad out to 56 mod 64 + if (count < 8) { + // Two lots of padding: Pad the first block to 64 bytes + memset(p, 0, count); + reverseBytes(m_in, 16); + MD5Transform(m_buf, reinterpret_cast_ptr<uint32_t *>(m_in)); // m_in is 4-byte aligned. + + // Now fill the next block with 56 bytes + memset(m_in, 0, 56); + } else { + // Pad block to 56 bytes + memset(p, 0, count - 8); + } + reverseBytes(m_in, 14); + + // Append length in bits and transform + // m_in is 4-byte aligned. + (reinterpret_cast_ptr<uint32_t*>(m_in))[14] = m_bits[0]; + (reinterpret_cast_ptr<uint32_t*>(m_in))[15] = m_bits[1]; + + MD5Transform(m_buf, reinterpret_cast_ptr<uint32_t*>(m_in)); + reverseBytes(reinterpret_cast<uint8_t*>(m_buf), 4); + + // Now, m_buf contains checksum result. + if (!digest.isEmpty()) + digest.clear(); + digest.append(reinterpret_cast<uint8_t*>(m_buf), 16); + + // In case it's sensitive + memset(m_buf, 0, sizeof(m_buf)); + memset(m_bits, 0, sizeof(m_bits)); + memset(m_in, 0, sizeof(m_in)); +} + +} // namespace WTF diff --git a/Source/JavaScriptCore/wtf/MD5.h b/Source/JavaScriptCore/wtf/MD5.h new file mode 100644 index 000000000..3caa810ba --- /dev/null +++ b/Source/JavaScriptCore/wtf/MD5.h @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2010 Google Inc. 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 Google Inc. 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. + */ + +#ifndef WTF_MD5_h +#define WTF_MD5_h + +#include <wtf/Vector.h> + +namespace WTF { + +class MD5 { +public: + MD5(); + + void addBytes(const Vector<uint8_t>& input) + { + addBytes(input.data(), input.size()); + } + void addBytes(const uint8_t* input, size_t length); + + // checksum has a side effect of resetting the state of the object. + void checksum(Vector<uint8_t, 16>&); + +private: + uint32_t m_buf[4]; + uint32_t m_bits[2]; + uint8_t m_in[64]; +}; + +} // namespace WTF + +using WTF::MD5; + +#endif // WTF_MD5_h diff --git a/Source/JavaScriptCore/wtf/MainThread.cpp b/Source/JavaScriptCore/wtf/MainThread.cpp new file mode 100644 index 000000000..915126589 --- /dev/null +++ b/Source/JavaScriptCore/wtf/MainThread.cpp @@ -0,0 +1,252 @@ +/* + * Copyright (C) 2007, 2008 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of Apple Computer, Inc. ("Apple") 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 APPLE AND ITS 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 APPLE OR ITS 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 "config.h" +#include "MainThread.h" + +#include "CurrentTime.h" +#include "Deque.h" +#include "Functional.h" +#include "StdLibExtras.h" +#include "Threading.h" + +#if PLATFORM(CHROMIUM) +#error Chromium uses a different main thread implementation +#endif + +namespace WTF { + +struct FunctionWithContext { + MainThreadFunction* function; + void* context; + ThreadCondition* syncFlag; + + FunctionWithContext(MainThreadFunction* function = 0, void* context = 0, ThreadCondition* syncFlag = 0) + : function(function) + , context(context) + , syncFlag(syncFlag) + { + } + bool operator == (const FunctionWithContext& o) + { + return function == o.function + && context == o.context + && syncFlag == o.syncFlag; + } +}; + +class FunctionWithContextFinder { +public: + FunctionWithContextFinder(const FunctionWithContext& m) : m(m) {} + bool operator() (FunctionWithContext& o) { return o == m; } + FunctionWithContext m; +}; + + +typedef Deque<FunctionWithContext> FunctionQueue; + +static bool callbacksPaused; // This global variable is only accessed from main thread. +#if !PLATFORM(MAC) +static ThreadIdentifier mainThreadIdentifier; +#endif + +static Mutex& mainThreadFunctionQueueMutex() +{ + DEFINE_STATIC_LOCAL(Mutex, staticMutex, ()); + return staticMutex; +} + +static FunctionQueue& functionQueue() +{ + DEFINE_STATIC_LOCAL(FunctionQueue, staticFunctionQueue, ()); + return staticFunctionQueue; +} + + +#if !PLATFORM(MAC) + +void initializeMainThread() +{ + static bool initializedMainThread; + if (initializedMainThread) + return; + initializedMainThread = true; + + mainThreadIdentifier = currentThread(); + + mainThreadFunctionQueueMutex(); + initializeMainThreadPlatform(); +} + +#else + +static pthread_once_t initializeMainThreadKeyOnce = PTHREAD_ONCE_INIT; + +static void initializeMainThreadOnce() +{ + mainThreadFunctionQueueMutex(); + initializeMainThreadPlatform(); +} + +void initializeMainThread() +{ + pthread_once(&initializeMainThreadKeyOnce, initializeMainThreadOnce); +} + +static void initializeMainThreadToProcessMainThreadOnce() +{ + mainThreadFunctionQueueMutex(); + initializeMainThreadToProcessMainThreadPlatform(); +} + +void initializeMainThreadToProcessMainThread() +{ + pthread_once(&initializeMainThreadKeyOnce, initializeMainThreadToProcessMainThreadOnce); +} +#endif + +// 0.1 sec delays in UI is approximate threshold when they become noticeable. Have a limit that's half of that. +static const double maxRunLoopSuspensionTime = 0.05; + +void dispatchFunctionsFromMainThread() +{ + ASSERT(isMainThread()); + + if (callbacksPaused) + return; + + double startTime = currentTime(); + + FunctionWithContext invocation; + while (true) { + { + MutexLocker locker(mainThreadFunctionQueueMutex()); + if (!functionQueue().size()) + break; + invocation = functionQueue().takeFirst(); + } + + invocation.function(invocation.context); + if (invocation.syncFlag) { + MutexLocker locker(mainThreadFunctionQueueMutex()); + invocation.syncFlag->signal(); + } + + // If we are running accumulated functions for too long so UI may become unresponsive, we need to + // yield so the user input can be processed. Otherwise user may not be able to even close the window. + // This code has effect only in case the scheduleDispatchFunctionsOnMainThread() is implemented in a way that + // allows input events to be processed before we are back here. + if (currentTime() - startTime > maxRunLoopSuspensionTime) { + scheduleDispatchFunctionsOnMainThread(); + break; + } + } +} + +void callOnMainThread(MainThreadFunction* function, void* context) +{ + ASSERT(function); + bool needToSchedule = false; + { + MutexLocker locker(mainThreadFunctionQueueMutex()); + needToSchedule = functionQueue().size() == 0; + functionQueue().append(FunctionWithContext(function, context)); + } + if (needToSchedule) + scheduleDispatchFunctionsOnMainThread(); +} + +void callOnMainThreadAndWait(MainThreadFunction* function, void* context) +{ + ASSERT(function); + + if (isMainThread()) { + function(context); + return; + } + + ThreadCondition syncFlag; + Mutex& functionQueueMutex = mainThreadFunctionQueueMutex(); + MutexLocker locker(functionQueueMutex); + functionQueue().append(FunctionWithContext(function, context, &syncFlag)); + if (functionQueue().size() == 1) + scheduleDispatchFunctionsOnMainThread(); + syncFlag.wait(functionQueueMutex); +} + +void cancelCallOnMainThread(MainThreadFunction* function, void* context) +{ + ASSERT(function); + + MutexLocker locker(mainThreadFunctionQueueMutex()); + + FunctionWithContextFinder pred(FunctionWithContext(function, context)); + + while (true) { + // We must redefine 'i' each pass, because the itererator's operator= + // requires 'this' to be valid, and remove() invalidates all iterators + FunctionQueue::iterator i(functionQueue().findIf(pred)); + if (i == functionQueue().end()) + break; + functionQueue().remove(i); + } +} + +static void callFunctionObject(void* context) +{ + Function<void ()>* function = static_cast<Function<void ()>*>(context); + (*function)(); + delete function; +} + +void callOnMainThread(const Function<void ()>& function) +{ + callOnMainThread(callFunctionObject, new Function<void ()>(function)); +} + +void setMainThreadCallbacksPaused(bool paused) +{ + ASSERT(isMainThread()); + + if (callbacksPaused == paused) + return; + + callbacksPaused = paused; + + if (!callbacksPaused) + scheduleDispatchFunctionsOnMainThread(); +} + +#if !PLATFORM(MAC) +bool isMainThread() +{ + return currentThread() == mainThreadIdentifier; +} +#endif + +} // namespace WTF diff --git a/Source/JavaScriptCore/wtf/MainThread.h b/Source/JavaScriptCore/wtf/MainThread.h new file mode 100644 index 000000000..c153d1eba --- /dev/null +++ b/Source/JavaScriptCore/wtf/MainThread.h @@ -0,0 +1,85 @@ +/* + * Copyright (C) 2007, 2008, 2010 Apple Inc. All rights reserved. + * Copyright (C) 2007 Justin Haygood (jhaygood@reaktix.com) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of Apple Computer, Inc. ("Apple") 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 APPLE AND ITS 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 APPLE OR ITS 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. + */ + +#ifndef MainThread_h +#define MainThread_h + +#include "Platform.h" + +#include <stdint.h> + +namespace WTF { + +typedef uint32_t ThreadIdentifier; +typedef void MainThreadFunction(void*); + +// Must be called from the main thread. +void initializeMainThread(); + +void callOnMainThread(MainThreadFunction*, void* context); +void callOnMainThreadAndWait(MainThreadFunction*, void* context); +void cancelCallOnMainThread(MainThreadFunction*, void* context); + +template<typename> class Function; +void callOnMainThread(const Function<void ()>&); + +void setMainThreadCallbacksPaused(bool paused); + +bool isMainThread(); +#if ENABLE(PARALLEL_GC) +void registerGCThread(); +bool isMainThreadOrGCThread(); +#elif PLATFORM(MAC) +bool isMainThreadOrGCThread(); +#else +inline bool isMainThreadOrGCThread() { return isMainThread(); } +#endif + +// NOTE: these functions are internal to the callOnMainThread implementation. +void initializeMainThreadPlatform(); +void scheduleDispatchFunctionsOnMainThread(); +void dispatchFunctionsFromMainThread(); + +#if PLATFORM(MAC) +// This version of initializeMainThread sets up the main thread as corresponding +// to the process's main thread, and not necessarily the thread that calls this +// function. It should only be used as a legacy aid for Mac WebKit. +void initializeMainThreadToProcessMainThread(); +void initializeMainThreadToProcessMainThreadPlatform(); +#endif + +} // namespace WTF + +using WTF::callOnMainThread; +using WTF::callOnMainThreadAndWait; +using WTF::cancelCallOnMainThread; +using WTF::setMainThreadCallbacksPaused; +using WTF::isMainThread; +using WTF::isMainThreadOrGCThread; +#endif // MainThread_h diff --git a/Source/JavaScriptCore/wtf/MallocZoneSupport.h b/Source/JavaScriptCore/wtf/MallocZoneSupport.h new file mode 100644 index 000000000..4332e40b8 --- /dev/null +++ b/Source/JavaScriptCore/wtf/MallocZoneSupport.h @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2007 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of Apple Computer, Inc. ("Apple") 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 APPLE AND ITS 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 APPLE OR ITS 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. + */ + +#ifndef MallocZoneSupport_h +#define MallocZoneSupport_h + +#include <malloc/malloc.h> + +namespace WTF { + +class RemoteMemoryReader { + task_t m_task; + memory_reader_t* m_reader; + +public: + RemoteMemoryReader(task_t task, memory_reader_t* reader) + : m_task(task) + , m_reader(reader) + { } + + void* operator()(vm_address_t address, size_t size) const + { + void* output; + kern_return_t err = (*m_reader)(m_task, address, size, static_cast<void**>(&output)); + if (err) + output = 0; + return output; + } + + template <typename T> + T* operator()(T* address, size_t size=sizeof(T)) const + { + return static_cast<T*>((*this)(reinterpret_cast<vm_address_t>(address), size)); + } + + template <typename T> + T* nextEntryInLinkedList(T** address) const + { + T** output = (*this)(address); + if (!output) + return 0; + return *output; + } +}; + +} // namespace WTF + +#endif // MallocZoneSupport_h diff --git a/Source/JavaScriptCore/wtf/MathExtras.h b/Source/JavaScriptCore/wtf/MathExtras.h new file mode 100644 index 000000000..e8ebd6ba1 --- /dev/null +++ b/Source/JavaScriptCore/wtf/MathExtras.h @@ -0,0 +1,331 @@ +/* + * Copyright (C) 2006, 2007, 2008, 2009, 2010 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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 APPLE COMPUTER, INC. ``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 APPLE COMPUTER, INC. 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. + */ + +#ifndef WTF_MathExtras_h +#define WTF_MathExtras_h + +#include <algorithm> +#include <cmath> +#include <float.h> +#include <limits> +#include <stdint.h> +#include <stdlib.h> +#include <wtf/StdLibExtras.h> + +#if OS(SOLARIS) +#include <ieeefp.h> +#endif + +#if OS(OPENBSD) +#include <sys/types.h> +#include <machine/ieee.h> +#endif + +#if COMPILER(MSVC) +#if OS(WINCE) +#include <stdlib.h> +#endif +#include <limits> +#endif + +#if OS(QNX) +// FIXME: Look into a way to have cmath import its functions into both the standard and global +// namespace. For now, we include math.h since the QNX cmath header only imports its functions +// into the standard namespace. +#include <math.h> +#endif + +#ifndef M_PI +const double piDouble = 3.14159265358979323846; +const float piFloat = 3.14159265358979323846f; +#else +const double piDouble = M_PI; +const float piFloat = static_cast<float>(M_PI); +#endif + +#ifndef M_PI_2 +const double piOverTwoDouble = 1.57079632679489661923; +const float piOverTwoFloat = 1.57079632679489661923f; +#else +const double piOverTwoDouble = M_PI_2; +const float piOverTwoFloat = static_cast<float>(M_PI_2); +#endif + +#ifndef M_PI_4 +const double piOverFourDouble = 0.785398163397448309616; +const float piOverFourFloat = 0.785398163397448309616f; +#else +const double piOverFourDouble = M_PI_4; +const float piOverFourFloat = static_cast<float>(M_PI_4); +#endif + +#if OS(DARWIN) + +// Work around a bug in the Mac OS X libc where ceil(-0.1) return +0. +inline double wtf_ceil(double x) { return copysign(ceil(x), x); } + +#define ceil(x) wtf_ceil(x) + +#endif + +#if OS(SOLARIS) + +#ifndef isfinite +inline bool isfinite(double x) { return finite(x) && !isnand(x); } +#endif +#ifndef isinf +inline bool isinf(double x) { return !finite(x) && !isnand(x); } +#endif +#ifndef signbit +inline bool signbit(double x) { return copysign(1.0, x) < 0; } +#endif + +#endif + +#if OS(OPENBSD) + +#ifndef isfinite +inline bool isfinite(double x) { return finite(x); } +#endif +#ifndef signbit +inline bool signbit(double x) { struct ieee_double *p = (struct ieee_double *)&x; return p->dbl_sign; } +#endif + +#endif + +#if COMPILER(MSVC) || (COMPILER(RVCT) && !(RVCT_VERSION_AT_LEAST(3, 0, 0, 0))) + +// We must not do 'num + 0.5' or 'num - 0.5' because they can cause precision loss. +static double round(double num) +{ + double integer = ceil(num); + if (num > 0) + return integer - num > 0.5 ? integer - 1.0 : integer; + return integer - num >= 0.5 ? integer - 1.0 : integer; +} +static float roundf(float num) +{ + float integer = ceilf(num); + if (num > 0) + return integer - num > 0.5f ? integer - 1.0f : integer; + return integer - num >= 0.5f ? integer - 1.0f : integer; +} +inline long long llround(double num) { return static_cast<long long>(round(num)); } +inline long long llroundf(float num) { return static_cast<long long>(roundf(num)); } +inline long lround(double num) { return static_cast<long>(round(num)); } +inline long lroundf(float num) { return static_cast<long>(roundf(num)); } +inline double trunc(double num) { return num > 0 ? floor(num) : ceil(num); } + +#endif + +#if COMPILER(GCC) && OS(QNX) +// The stdlib on QNX doesn't contain long abs(long). See PR #104666. +inline long long abs(long num) { return labs(num); } +#endif + +#if COMPILER(MSVC) +// The 64bit version of abs() is already defined in stdlib.h which comes with VC10 +#if COMPILER(MSVC9_OR_LOWER) +inline long long abs(long long num) { return _abs64(num); } +#endif + +inline bool isinf(double num) { return !_finite(num) && !_isnan(num); } +inline bool isnan(double num) { return !!_isnan(num); } +inline bool signbit(double num) { return _copysign(1.0, num) < 0; } + +inline double nextafter(double x, double y) { return _nextafter(x, y); } +inline float nextafterf(float x, float y) { return x > y ? x - FLT_EPSILON : x + FLT_EPSILON; } + +inline double copysign(double x, double y) { return _copysign(x, y); } +inline int isfinite(double x) { return _finite(x); } + +// MSVC's math.h does not currently supply log2 or log2f. +inline double log2(double num) +{ + // This constant is roughly M_LN2, which is not provided by default on Windows. + return log(num) / 0.693147180559945309417232121458176568; +} + +inline float log2f(float num) +{ + // This constant is roughly M_LN2, which is not provided by default on Windows. + return logf(num) / 0.693147180559945309417232121458176568f; +} + +// Work around a bug in Win, where atan2(+-infinity, +-infinity) yields NaN instead of specific values. +inline double wtf_atan2(double x, double y) +{ + double posInf = std::numeric_limits<double>::infinity(); + double negInf = -std::numeric_limits<double>::infinity(); + double nan = std::numeric_limits<double>::quiet_NaN(); + + double result = nan; + + if (x == posInf && y == posInf) + result = piOverFourDouble; + else if (x == posInf && y == negInf) + result = 3 * piOverFourDouble; + else if (x == negInf && y == posInf) + result = -piOverFourDouble; + else if (x == negInf && y == negInf) + result = -3 * piOverFourDouble; + else + result = ::atan2(x, y); + + return result; +} + +// Work around a bug in the Microsoft CRT, where fmod(x, +-infinity) yields NaN instead of x. +inline double wtf_fmod(double x, double y) { return (!isinf(x) && isinf(y)) ? x : fmod(x, y); } + +// Work around a bug in the Microsoft CRT, where pow(NaN, 0) yields NaN instead of 1. +inline double wtf_pow(double x, double y) { return y == 0 ? 1 : pow(x, y); } + +#define atan2(x, y) wtf_atan2(x, y) +#define fmod(x, y) wtf_fmod(x, y) +#define pow(x, y) wtf_pow(x, y) + +#endif // COMPILER(MSVC) + +inline double deg2rad(double d) { return d * piDouble / 180.0; } +inline double rad2deg(double r) { return r * 180.0 / piDouble; } +inline double deg2grad(double d) { return d * 400.0 / 360.0; } +inline double grad2deg(double g) { return g * 360.0 / 400.0; } +inline double turn2deg(double t) { return t * 360.0; } +inline double deg2turn(double d) { return d / 360.0; } +inline double rad2grad(double r) { return r * 200.0 / piDouble; } +inline double grad2rad(double g) { return g * piDouble / 200.0; } + +inline float deg2rad(float d) { return d * piFloat / 180.0f; } +inline float rad2deg(float r) { return r * 180.0f / piFloat; } +inline float deg2grad(float d) { return d * 400.0f / 360.0f; } +inline float grad2deg(float g) { return g * 360.0f / 400.0f; } +inline float turn2deg(float t) { return t * 360.0f; } +inline float deg2turn(float d) { return d / 360.0f; } +inline float rad2grad(float r) { return r * 200.0f / piFloat; } +inline float grad2rad(float g) { return g * piFloat / 200.0f; } + +// std::numeric_limits<T>::min() returns the smallest positive value for floating point types +template<typename T> inline T defaultMinimumForClamp() { return std::numeric_limits<T>::min(); } +template<> inline float defaultMinimumForClamp() { return -std::numeric_limits<float>::max(); } +template<> inline double defaultMinimumForClamp() { return -std::numeric_limits<double>::max(); } +template<typename T> inline T defaultMaximumForClamp() { return std::numeric_limits<T>::max(); } + +template<typename T> inline T clampTo(double value, T min = defaultMinimumForClamp<T>(), T max = defaultMaximumForClamp<T>()) +{ + if (value >= static_cast<double>(max)) + return max; + if (value <= static_cast<double>(min)) + return min; + return static_cast<T>(value); +} +template<> inline long long int clampTo(double, long long int, long long int); // clampTo does not support long long ints. + +inline int clampToInteger(double value) +{ + return clampTo<int>(value); +} + +inline float clampToFloat(double value) +{ + return clampTo<float>(value); +} + +inline int clampToPositiveInteger(double value) +{ + return clampTo<int>(value, 0); +} + +inline int clampToInteger(float value) +{ + return clampTo<int>(value); +} + +inline int clampToInteger(unsigned x) +{ + const unsigned intMax = static_cast<unsigned>(std::numeric_limits<int>::max()); + + if (x >= intMax) + return std::numeric_limits<int>::max(); + return static_cast<int>(x); +} + +inline bool isWithinIntRange(float x) +{ + return x > static_cast<float>(std::numeric_limits<int>::min()) && x < static_cast<float>(std::numeric_limits<int>::max()); +} + +#if !COMPILER(MSVC) && !COMPILER(RVCT) && !OS(SOLARIS) +using std::isfinite; +using std::isinf; +using std::isnan; +using std::signbit; +#endif + +// decompose 'number' to its sign, exponent, and mantissa components. +// The result is interpreted as: +// (sign ? -1 : 1) * pow(2, exponent) * (mantissa / (1 << 52)) +inline void decomposeDouble(double number, bool& sign, int32_t& exponent, uint64_t& mantissa) +{ + ASSERT(isfinite(number)); + + sign = signbit(number); + + uint64_t bits = WTF::bitwise_cast<uint64_t>(number); + exponent = (static_cast<int32_t>(bits >> 52) & 0x7ff) - 0x3ff; + mantissa = bits & 0xFFFFFFFFFFFFFull; + + // Check for zero/denormal values; if so, adjust the exponent, + // if not insert the implicit, omitted leading 1 bit. + if (exponent == -0x3ff) + exponent = mantissa ? -0x3fe : 0; + else + mantissa |= 0x10000000000000ull; +} + +// Calculate d % 2^{64}. +inline void doubleToInteger(double d, unsigned long long& value) +{ + if (isnan(d) || isinf(d)) + value = 0; + else { + // -2^{64} < fmodValue < 2^{64}. + double fmodValue = fmod(trunc(d), std::numeric_limits<unsigned long long>::max() + 1.0); + if (fmodValue >= 0) { + // 0 <= fmodValue < 2^{64}. + // 0 <= value < 2^{64}. This cast causes no loss. + value = static_cast<unsigned long long>(fmodValue); + } else { + // -2^{64} < fmodValue < 0. + // 0 < fmodValueInUnsignedLongLong < 2^{64}. This cast causes no loss. + unsigned long long fmodValueInUnsignedLongLong = static_cast<unsigned long long>(-fmodValue); + // -1 < (std::numeric_limits<unsigned long long>::max() - fmodValueInUnsignedLongLong) < 2^{64} - 1. + // 0 < value < 2^{64}. + value = std::numeric_limits<unsigned long long>::max() - fmodValueInUnsignedLongLong + 1; + } + } +} + +#endif // #ifndef WTF_MathExtras_h diff --git a/Source/JavaScriptCore/wtf/MessageQueue.h b/Source/JavaScriptCore/wtf/MessageQueue.h new file mode 100644 index 000000000..dda852fe1 --- /dev/null +++ b/Source/JavaScriptCore/wtf/MessageQueue.h @@ -0,0 +1,226 @@ +/* + * Copyright (C) 2008 Apple Inc. All rights reserved. + * Copyright (C) 2009 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of Apple Computer, Inc. ("Apple") 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 APPLE AND ITS 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 APPLE OR ITS 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. + */ + +#ifndef MessageQueue_h +#define MessageQueue_h + +#include <limits> +#include <wtf/Assertions.h> +#include <wtf/Deque.h> +#include <wtf/Noncopyable.h> +#include <wtf/Threading.h> + +namespace WTF { + + enum MessageQueueWaitResult { + MessageQueueTerminated, // Queue was destroyed while waiting for message. + MessageQueueTimeout, // Timeout was specified and it expired. + MessageQueueMessageReceived // A message was successfully received and returned. + }; + + // The queue takes ownership of messages and transfer it to the new owner + // when messages are fetched from the queue. + // Essentially, MessageQueue acts as a queue of OwnPtr<DataType>. + template<typename DataType> + class MessageQueue { + WTF_MAKE_NONCOPYABLE(MessageQueue); + public: + MessageQueue() : m_killed(false) { } + ~MessageQueue(); + + void append(PassOwnPtr<DataType>); + bool appendAndCheckEmpty(PassOwnPtr<DataType>); + void prepend(PassOwnPtr<DataType>); + + PassOwnPtr<DataType> waitForMessage(); + PassOwnPtr<DataType> tryGetMessage(); + PassOwnPtr<DataType> tryGetMessageIgnoringKilled(); + template<typename Predicate> + PassOwnPtr<DataType> waitForMessageFilteredWithTimeout(MessageQueueWaitResult&, Predicate&, double absoluteTime); + + template<typename Predicate> + void removeIf(Predicate&); + + void kill(); + bool killed() const; + + // The result of isEmpty() is only valid if no other thread is manipulating the queue at the same time. + bool isEmpty(); + + static double infiniteTime() { return std::numeric_limits<double>::max(); } + + private: + static bool alwaysTruePredicate(DataType*) { return true; } + + mutable Mutex m_mutex; + ThreadCondition m_condition; + Deque<DataType*> m_queue; + bool m_killed; + }; + + template<typename DataType> + MessageQueue<DataType>::~MessageQueue() + { + deleteAllValues(m_queue); + } + + template<typename DataType> + inline void MessageQueue<DataType>::append(PassOwnPtr<DataType> message) + { + MutexLocker lock(m_mutex); + m_queue.append(message.leakPtr()); + m_condition.signal(); + } + + // Returns true if the queue was empty before the item was added. + template<typename DataType> + inline bool MessageQueue<DataType>::appendAndCheckEmpty(PassOwnPtr<DataType> message) + { + MutexLocker lock(m_mutex); + bool wasEmpty = m_queue.isEmpty(); + m_queue.append(message.leakPtr()); + m_condition.signal(); + return wasEmpty; + } + + template<typename DataType> + inline void MessageQueue<DataType>::prepend(PassOwnPtr<DataType> message) + { + MutexLocker lock(m_mutex); + m_queue.prepend(message.leakPtr()); + m_condition.signal(); + } + + template<typename DataType> + inline PassOwnPtr<DataType> MessageQueue<DataType>::waitForMessage() + { + MessageQueueWaitResult exitReason; + OwnPtr<DataType> result = waitForMessageFilteredWithTimeout(exitReason, MessageQueue<DataType>::alwaysTruePredicate, infiniteTime()); + ASSERT(exitReason == MessageQueueTerminated || exitReason == MessageQueueMessageReceived); + return result.release(); + } + + template<typename DataType> + template<typename Predicate> + inline PassOwnPtr<DataType> MessageQueue<DataType>::waitForMessageFilteredWithTimeout(MessageQueueWaitResult& result, Predicate& predicate, double absoluteTime) + { + MutexLocker lock(m_mutex); + bool timedOut = false; + + DequeConstIterator<DataType*> found = m_queue.end(); + while (!m_killed && !timedOut && (found = m_queue.findIf(predicate)) == m_queue.end()) + timedOut = !m_condition.timedWait(m_mutex, absoluteTime); + + ASSERT(!timedOut || absoluteTime != infiniteTime()); + + if (m_killed) { + result = MessageQueueTerminated; + return nullptr; + } + + if (timedOut) { + result = MessageQueueTimeout; + return nullptr; + } + + ASSERT(found != m_queue.end()); + OwnPtr<DataType> message = adoptPtr(*found); + m_queue.remove(found); + result = MessageQueueMessageReceived; + return message.release(); + } + + template<typename DataType> + inline PassOwnPtr<DataType> MessageQueue<DataType>::tryGetMessage() + { + MutexLocker lock(m_mutex); + if (m_killed) + return nullptr; + if (m_queue.isEmpty()) + return nullptr; + + return adoptPtr(m_queue.takeFirst()); + } + + template<typename DataType> + inline PassOwnPtr<DataType> MessageQueue<DataType>::tryGetMessageIgnoringKilled() + { + MutexLocker lock(m_mutex); + if (m_queue.isEmpty()) + return nullptr; + + return adoptPtr(m_queue.takeFirst()); + } + + template<typename DataType> + template<typename Predicate> + inline void MessageQueue<DataType>::removeIf(Predicate& predicate) + { + MutexLocker lock(m_mutex); + DequeConstIterator<DataType*> found = m_queue.end(); + while ((found = m_queue.findIf(predicate)) != m_queue.end()) { + DataType* message = *found; + m_queue.remove(found); + delete message; + } + } + + template<typename DataType> + inline bool MessageQueue<DataType>::isEmpty() + { + MutexLocker lock(m_mutex); + if (m_killed) + return true; + return m_queue.isEmpty(); + } + + template<typename DataType> + inline void MessageQueue<DataType>::kill() + { + MutexLocker lock(m_mutex); + m_killed = true; + m_condition.broadcast(); + } + + template<typename DataType> + inline bool MessageQueue<DataType>::killed() const + { + MutexLocker lock(m_mutex); + return m_killed; + } +} // namespace WTF + +using WTF::MessageQueue; +// MessageQueueWaitResult enum and all its values. +using WTF::MessageQueueWaitResult; +using WTF::MessageQueueTerminated; +using WTF::MessageQueueTimeout; +using WTF::MessageQueueMessageReceived; + +#endif // MessageQueue_h diff --git a/Source/JavaScriptCore/wtf/MetaAllocator.cpp b/Source/JavaScriptCore/wtf/MetaAllocator.cpp new file mode 100644 index 000000000..92205a5d8 --- /dev/null +++ b/Source/JavaScriptCore/wtf/MetaAllocator.cpp @@ -0,0 +1,420 @@ +/* + * Copyright (C) 2011 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of Apple Computer, Inc. ("Apple") 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 APPLE AND ITS 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 APPLE OR ITS 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 "config.h" +#include "MetaAllocator.h" + +#include <wtf/FastMalloc.h> + +namespace WTF { + +MetaAllocatorHandle::MetaAllocatorHandle(MetaAllocator* allocator, void* start, size_t sizeInBytes) + : m_allocator(allocator) + , m_start(start) + , m_sizeInBytes(sizeInBytes) +{ + ASSERT(allocator); + ASSERT(start); + ASSERT(sizeInBytes); +} + +MetaAllocatorHandle::~MetaAllocatorHandle() +{ + if (!m_allocator) + return; + SpinLockHolder locker(&m_allocator->m_lock); + if (m_sizeInBytes) { + m_allocator->decrementPageOccupancy(m_start, m_sizeInBytes); + m_allocator->addFreeSpaceFromReleasedHandle(m_start, m_sizeInBytes); + } +} + +void MetaAllocatorHandle::shrink(size_t newSizeInBytes) +{ + ASSERT(newSizeInBytes <= m_sizeInBytes); + + if (!m_allocator) { + m_sizeInBytes = newSizeInBytes; + return; + } + + SpinLockHolder locker(&m_allocator->m_lock); + + newSizeInBytes = m_allocator->roundUp(newSizeInBytes); + + ASSERT(newSizeInBytes <= m_sizeInBytes); + + if (newSizeInBytes == m_sizeInBytes) + return; + + uintptr_t freeStart = reinterpret_cast<uintptr_t>(m_start) + newSizeInBytes; + size_t freeSize = m_sizeInBytes - newSizeInBytes; + uintptr_t freeEnd = freeStart + freeSize; + + uintptr_t firstCompletelyFreePage = (freeStart + m_allocator->m_pageSize - 1) & ~(m_allocator->m_pageSize - 1); + if (firstCompletelyFreePage < freeEnd) + m_allocator->decrementPageOccupancy(reinterpret_cast<void*>(firstCompletelyFreePage), freeSize - (firstCompletelyFreePage - freeStart)); + + m_allocator->addFreeSpaceFromReleasedHandle(reinterpret_cast<void*>(freeStart), freeSize); + + m_sizeInBytes = newSizeInBytes; +} + +MetaAllocator::MetaAllocator(size_t allocationGranule) + : m_allocationGranule(allocationGranule) + , m_pageSize(pageSize()) + , m_bytesAllocated(0) + , m_bytesReserved(0) + , m_bytesCommitted(0) +#ifndef NDEBUG + , m_mallocBalance(0) +#endif +#if ENABLE(META_ALLOCATOR_PROFILE) + , m_numAllocations(0) + , m_numFrees(0) +#endif +{ + m_lock.Init(); + + for (m_logPageSize = 0; m_logPageSize < 32; ++m_logPageSize) { + if (static_cast<size_t>(1) << m_logPageSize == m_pageSize) + break; + } + + ASSERT(static_cast<size_t>(1) << m_logPageSize == m_pageSize); + + for (m_logAllocationGranule = 0; m_logAllocationGranule < 32; ++m_logAllocationGranule) { + if (static_cast<size_t>(1) << m_logAllocationGranule == m_allocationGranule) + break; + } + + ASSERT(static_cast<size_t>(1) << m_logAllocationGranule == m_allocationGranule); +} + +PassRefPtr<MetaAllocatorHandle> MetaAllocator::allocate(size_t sizeInBytes) +{ + SpinLockHolder locker(&m_lock); + + if (!sizeInBytes) + return 0; + + sizeInBytes = roundUp(sizeInBytes); + + void* start = findAndRemoveFreeSpace(sizeInBytes); + if (!start) { + size_t requestedNumberOfPages = (sizeInBytes + m_pageSize - 1) >> m_logPageSize; + size_t numberOfPages = requestedNumberOfPages; + + start = allocateNewSpace(numberOfPages); + if (!start) + return 0; + + ASSERT(numberOfPages >= requestedNumberOfPages); + + size_t roundedUpSize = numberOfPages << m_logPageSize; + + ASSERT(roundedUpSize >= sizeInBytes); + + m_bytesReserved += roundedUpSize; + + if (roundedUpSize > sizeInBytes) { + void* freeSpaceStart = reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(start) + sizeInBytes); + size_t freeSpaceSize = roundedUpSize - sizeInBytes; + addFreeSpace(freeSpaceStart, freeSpaceSize); + } + } + incrementPageOccupancy(start, sizeInBytes); + m_bytesAllocated += sizeInBytes; +#if ENABLE(META_ALLOCATOR_PROFILE) + m_numAllocations++; +#endif + + MetaAllocatorHandle* handle = new MetaAllocatorHandle(this, start, sizeInBytes); + // FIXME: Implement a verifier scheme that groks MetaAllocatorHandles + handle->deprecatedTurnOffVerifier(); + + return adoptRef(handle); +} + +MetaAllocator::Statistics MetaAllocator::currentStatistics() +{ + SpinLockHolder locker(&m_lock); + Statistics result; + result.bytesAllocated = m_bytesAllocated; + result.bytesReserved = m_bytesReserved; + result.bytesCommitted = m_bytesCommitted; + return result; +} + +void* MetaAllocator::findAndRemoveFreeSpace(size_t sizeInBytes) +{ + FreeSpaceNode* node = m_freeSpaceSizeMap.findLeastGreaterThanOrEqual(sizeInBytes); + + if (!node) + return 0; + + ASSERT(node->m_key >= sizeInBytes); + + m_freeSpaceSizeMap.remove(node); + + void* result; + + if (node->m_key == sizeInBytes) { + // Easy case: perfect fit, so just remove the node entirely. + result = node->m_value; + + m_freeSpaceStartAddressMap.remove(node->m_value); + m_freeSpaceEndAddressMap.remove(reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(node->m_value) + node->m_key)); + freeFreeSpaceNode(node); + } else { + // Try to be a good citizen and ensure that the returned chunk of memory + // straddles as few pages as possible, but only insofar as doing so will + // not increase fragmentation. The intuition is that minimizing + // fragmentation is a strictly higher priority than minimizing the number + // of committed pages, since in the long run, smaller fragmentation means + // fewer committed pages and fewer failures in general. + + uintptr_t firstPage = reinterpret_cast<uintptr_t>(node->m_value) >> m_logPageSize; + uintptr_t lastPage = (reinterpret_cast<uintptr_t>(node->m_value) + node->m_key - 1) >> m_logPageSize; + + uintptr_t lastPageForLeftAllocation = (reinterpret_cast<uintptr_t>(node->m_value) + sizeInBytes - 1) >> m_logPageSize; + uintptr_t firstPageForRightAllocation = (reinterpret_cast<uintptr_t>(node->m_value) + node->m_key - sizeInBytes) >> m_logPageSize; + + if (lastPageForLeftAllocation - firstPage + 1 <= lastPage - firstPageForRightAllocation + 1) { + // Allocate in the left side of the returned chunk, and slide the node to the right. + result = node->m_value; + + m_freeSpaceStartAddressMap.remove(node->m_value); + + node->m_key -= sizeInBytes; + node->m_value = reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(node->m_value) + sizeInBytes); + + m_freeSpaceSizeMap.insert(node); + m_freeSpaceStartAddressMap.add(node->m_value, node); + } else { + // Allocate in the right size of the returned chunk, and slide the node to the left; + + result = reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(node->m_value) + node->m_key - sizeInBytes); + + m_freeSpaceEndAddressMap.remove(reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(node->m_value) + node->m_key)); + + node->m_key -= sizeInBytes; + + m_freeSpaceSizeMap.insert(node); + m_freeSpaceEndAddressMap.add(result, node); + } + } + + return result; +} + +void MetaAllocator::addFreeSpaceFromReleasedHandle(void* start, size_t sizeInBytes) +{ +#if ENABLE(META_ALLOCATOR_PROFILE) + m_numFrees++; +#endif + m_bytesAllocated -= sizeInBytes; + addFreeSpace(start, sizeInBytes); +} + +void MetaAllocator::addFreshFreeSpace(void* start, size_t sizeInBytes) +{ + SpinLockHolder locker(&m_lock); + m_bytesReserved += sizeInBytes; + addFreeSpace(start, sizeInBytes); +} + +size_t MetaAllocator::debugFreeSpaceSize() +{ +#ifndef NDEBUG + SpinLockHolder locker(&m_lock); + size_t result = 0; + for (FreeSpaceNode* node = m_freeSpaceSizeMap.first(); node; node = node->successor()) + result += node->m_key; + return result; +#else + CRASH(); + return 0; +#endif +} + +void MetaAllocator::addFreeSpace(void* start, size_t sizeInBytes) +{ + void* end = reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(start) + sizeInBytes); + + HashMap<void*, FreeSpaceNode*>::iterator leftNeighbor = m_freeSpaceEndAddressMap.find(start); + HashMap<void*, FreeSpaceNode*>::iterator rightNeighbor = m_freeSpaceStartAddressMap.find(end); + + if (leftNeighbor != m_freeSpaceEndAddressMap.end()) { + // We have something we can coalesce with on the left. Remove it from the tree, and + // remove its end from the end address map. + + ASSERT(reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(leftNeighbor->second->m_value) + leftNeighbor->second->m_key) == leftNeighbor->first); + + FreeSpaceNode* leftNode = leftNeighbor->second; + + void* leftStart = leftNode->m_value; + size_t leftSize = leftNode->m_key; + void* leftEnd = reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(leftStart) + leftSize); + + ASSERT(leftEnd == start); + + m_freeSpaceSizeMap.remove(leftNode); + m_freeSpaceEndAddressMap.remove(leftEnd); + + // Now check if there is also something to coalesce with on the right. + if (rightNeighbor != m_freeSpaceStartAddressMap.end()) { + // Freeing something in the middle of free blocks. Coalesce both left and + // right, whilst removing the right neighbor from the maps. + + ASSERT(rightNeighbor->second->m_value == rightNeighbor->first); + + FreeSpaceNode* rightNode = rightNeighbor->second; + void* rightStart = rightNeighbor->first; + size_t rightSize = rightNode->m_key; + void* rightEnd = reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(rightStart) + rightSize); + + ASSERT(rightStart == end); + ASSERT(reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(leftStart) + leftSize + sizeInBytes + rightSize) == rightEnd); + + m_freeSpaceSizeMap.remove(rightNode); + m_freeSpaceStartAddressMap.remove(rightStart); + m_freeSpaceEndAddressMap.remove(rightEnd); + + freeFreeSpaceNode(rightNode); + + leftNode->m_key += sizeInBytes + rightSize; + + m_freeSpaceSizeMap.insert(leftNode); + m_freeSpaceEndAddressMap.add(rightEnd, leftNode); + } else { + leftNode->m_key += sizeInBytes; + + m_freeSpaceSizeMap.insert(leftNode); + m_freeSpaceEndAddressMap.add(end, leftNode); + } + } else { + // Cannot coalesce with left; try to see if we can coalesce with right. + + if (rightNeighbor != m_freeSpaceStartAddressMap.end()) { + FreeSpaceNode* rightNode = rightNeighbor->second; + void* rightStart = rightNeighbor->first; + size_t rightSize = rightNode->m_key; + void* rightEnd = reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(rightStart) + rightSize); + + ASSERT(rightStart == end); + ASSERT_UNUSED(rightEnd, reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(start) + sizeInBytes + rightSize) == rightEnd); + + m_freeSpaceSizeMap.remove(rightNode); + m_freeSpaceStartAddressMap.remove(rightStart); + + rightNode->m_key += sizeInBytes; + rightNode->m_value = start; + + m_freeSpaceSizeMap.insert(rightNode); + m_freeSpaceStartAddressMap.add(start, rightNode); + } else { + // Nothing to coalesce with, so create a new free space node and add it. + + FreeSpaceNode* node = allocFreeSpaceNode(); + + node->m_key = sizeInBytes; + node->m_value = start; + + m_freeSpaceSizeMap.insert(node); + m_freeSpaceStartAddressMap.add(start, node); + m_freeSpaceEndAddressMap.add(end, node); + } + } +} + +void MetaAllocator::incrementPageOccupancy(void* address, size_t sizeInBytes) +{ + uintptr_t firstPage = reinterpret_cast<uintptr_t>(address) >> m_logPageSize; + uintptr_t lastPage = (reinterpret_cast<uintptr_t>(address) + sizeInBytes - 1) >> m_logPageSize; + + for (uintptr_t page = firstPage; page <= lastPage; ++page) { + HashMap<uintptr_t, size_t>::iterator iter = m_pageOccupancyMap.find(page); + if (iter == m_pageOccupancyMap.end()) { + m_pageOccupancyMap.add(page, 1); + m_bytesCommitted += m_pageSize; + notifyNeedPage(reinterpret_cast<void*>(page << m_logPageSize)); + } else + iter->second++; + } +} + +void MetaAllocator::decrementPageOccupancy(void* address, size_t sizeInBytes) +{ + uintptr_t firstPage = reinterpret_cast<uintptr_t>(address) >> m_logPageSize; + uintptr_t lastPage = (reinterpret_cast<uintptr_t>(address) + sizeInBytes - 1) >> m_logPageSize; + + for (uintptr_t page = firstPage; page <= lastPage; ++page) { + HashMap<uintptr_t, size_t>::iterator iter = m_pageOccupancyMap.find(page); + ASSERT(iter != m_pageOccupancyMap.end()); + if (!--(iter->second)) { + m_pageOccupancyMap.remove(iter); + m_bytesCommitted -= m_pageSize; + notifyPageIsFree(reinterpret_cast<void*>(page << m_logPageSize)); + } + } +} + +size_t MetaAllocator::roundUp(size_t sizeInBytes) +{ + if (std::numeric_limits<size_t>::max() - m_allocationGranule <= sizeInBytes) + CRASH(); + return (sizeInBytes + m_allocationGranule - 1) & ~(m_allocationGranule - 1); +} + +MetaAllocator::FreeSpaceNode* MetaAllocator::allocFreeSpaceNode() +{ +#ifndef NDEBUG + m_mallocBalance++; +#endif + return new (NotNull, fastMalloc(sizeof(FreeSpaceNode))) FreeSpaceNode(0, 0); +} + +void MetaAllocator::freeFreeSpaceNode(FreeSpaceNode* node) +{ +#ifndef NDEBUG + m_mallocBalance--; +#endif + fastFree(node); +} + +#if ENABLE(META_ALLOCATOR_PROFILE) +void MetaAllocator::dumpProfile() +{ + printf("num allocations = %u, num frees = %u\n", m_numAllocations, m_numFrees); +} +#endif + +} // namespace WTF + + diff --git a/Source/JavaScriptCore/wtf/MetaAllocator.h b/Source/JavaScriptCore/wtf/MetaAllocator.h new file mode 100644 index 000000000..cf971b720 --- /dev/null +++ b/Source/JavaScriptCore/wtf/MetaAllocator.h @@ -0,0 +1,174 @@ +/* + * Copyright (C) 2011 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of Apple Computer, Inc. ("Apple") 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 APPLE AND ITS 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 APPLE OR ITS 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. + */ + +#ifndef WTF_MetaAllocator_h +#define WTF_MetaAllocator_h + +#include "Assertions.h" +#include "HashMap.h" +#include "MetaAllocatorHandle.h" +#include "Noncopyable.h" +#include "PageBlock.h" +#include "RedBlackTree.h" +#include "RefCounted.h" +#include "RefPtr.h" +#include "TCSpinLock.h" + +namespace WTF { + +#define ENABLE_META_ALLOCATOR_PROFILE 0 + +class MetaAllocator { + WTF_MAKE_NONCOPYABLE(MetaAllocator); +public: + + MetaAllocator(size_t allocationGranule); + + virtual ~MetaAllocator(); + + PassRefPtr<MetaAllocatorHandle> allocate(size_t sizeInBytes); + + // Non-atomic methods for getting allocator statistics. + size_t bytesAllocated() { return m_bytesAllocated; } + size_t bytesReserved() { return m_bytesReserved; } + size_t bytesCommitted() { return m_bytesCommitted; } + + // Atomic method for getting allocator statistics. + struct Statistics { + size_t bytesAllocated; + size_t bytesReserved; + size_t bytesCommitted; + }; + Statistics currentStatistics(); + + // Add more free space to the allocator. Call this directly from + // the constructor if you wish to operate the allocator within a + // fixed pool. + void addFreshFreeSpace(void* start, size_t sizeInBytes); + + // This is meant only for implementing tests. Never call this in release + // builds. + size_t debugFreeSpaceSize(); + +#if ENABLE(META_ALLOCATOR_PROFILE) + void dumpProfile(); +#else + void dumpProfile() { } +#endif + +protected: + + // Allocate new virtual space, but don't commit. This may return more + // pages than we asked, in which case numPages is changed. + virtual void* allocateNewSpace(size_t& numPages) = 0; + + // Commit a page. + virtual void notifyNeedPage(void* page) = 0; + + // Uncommit a page. + virtual void notifyPageIsFree(void* page) = 0; + + // NOTE: none of the above methods are called during allocator + // destruction, in part because a MetaAllocator cannot die so long + // as there are Handles that refer to it. + +private: + + friend class MetaAllocatorHandle; + + typedef RedBlackTree<size_t, void*> Tree; + typedef Tree::Node FreeSpaceNode; + + // Remove free space from the allocator. This is effectively + // the allocate() function, except that it does not mark the + // returned space as being in-use. + void* findAndRemoveFreeSpace(size_t sizeInBytes); + + // This is called when memory from an allocation is freed. + void addFreeSpaceFromReleasedHandle(void* start, size_t sizeInBytes); + + // This is the low-level implementation of adding free space; it + // is called from both addFreeSpaceFromReleasedHandle and from + // addFreshFreeSpace. + void addFreeSpace(void* start, size_t sizeInBytes); + + // Management of used space. + + void incrementPageOccupancy(void* address, size_t sizeInBytes); + void decrementPageOccupancy(void* address, size_t sizeInBytes); + + // Utilities. + + size_t roundUp(size_t sizeInBytes); + + FreeSpaceNode* allocFreeSpaceNode(); + void freeFreeSpaceNode(FreeSpaceNode*); + + size_t m_allocationGranule; + unsigned m_logAllocationGranule; + size_t m_pageSize; + unsigned m_logPageSize; + + Tree m_freeSpaceSizeMap; + HashMap<void*, FreeSpaceNode*> m_freeSpaceStartAddressMap; + HashMap<void*, FreeSpaceNode*> m_freeSpaceEndAddressMap; + HashMap<uintptr_t, size_t> m_pageOccupancyMap; + + size_t m_bytesAllocated; + size_t m_bytesReserved; + size_t m_bytesCommitted; + + SpinLock m_lock; + +#ifndef NDEBUG + size_t m_mallocBalance; +#endif + +#if ENABLE(META_ALLOCATOR_PROFILE) + unsigned m_numAllocations; + unsigned m_numFrees; +#endif +}; + +inline MetaAllocator::~MetaAllocator() +{ + for (FreeSpaceNode* node = m_freeSpaceSizeMap.first(); node;) { + FreeSpaceNode* next = node->successor(); + m_freeSpaceSizeMap.remove(node); + freeFreeSpaceNode(node); + node = next; + } +#ifndef NDEBUG + ASSERT(!m_mallocBalance); +#endif +} + +} // namespace WTF + +#endif // WTF_MetaAllocator_h + diff --git a/Source/JavaScriptCore/wtf/MetaAllocatorHandle.h b/Source/JavaScriptCore/wtf/MetaAllocatorHandle.h new file mode 100644 index 000000000..5a32081d4 --- /dev/null +++ b/Source/JavaScriptCore/wtf/MetaAllocatorHandle.h @@ -0,0 +1,98 @@ +/* + * Copyright (C) 2011 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of Apple Computer, Inc. ("Apple") 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 APPLE AND ITS 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 APPLE OR ITS 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. + */ + +#ifndef WTF_MetaAllocatorHandle_h +#define WTF_MetaAllocatorHandle_h + +#include <wtf/Assertions.h> +#include <wtf/RefCounted.h> +#include <wtf/RefPtr.h> + +namespace WTF { + +class MetaAllocator; + +class MetaAllocatorHandle : public RefCounted<MetaAllocatorHandle> { +private: + MetaAllocatorHandle(MetaAllocator*, void* start, size_t sizeInBytes); + + MetaAllocatorHandle(void* start, size_t sizeInBytes) + : m_allocator(0) + , m_start(start) + , m_sizeInBytes(sizeInBytes) + { + ASSERT(start); + } + +public: + ~MetaAllocatorHandle(); + + static PassRefPtr<MetaAllocatorHandle> createSelfManagedHandle(void* start, size_t sizeInBytes) + { + return adoptRef(new MetaAllocatorHandle(start, sizeInBytes)); + } + + void* start() + { + return m_start; + } + + void* end() + { + return reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(m_start) + m_sizeInBytes); + } + + size_t sizeInBytes() + { + return m_sizeInBytes; + } + + void shrink(size_t newSizeInBytes); + + bool isManaged() + { + return !!m_allocator; + } + + MetaAllocator* allocator() + { + ASSERT(m_allocator); + return m_allocator; + } + +private: + friend class MetaAllocator; + + MetaAllocator* m_allocator; + void* m_start; + size_t m_sizeInBytes; +}; + +} + +#endif diff --git a/Source/JavaScriptCore/wtf/NonCopyingSort.h b/Source/JavaScriptCore/wtf/NonCopyingSort.h new file mode 100644 index 000000000..fd611bde7 --- /dev/null +++ b/Source/JavaScriptCore/wtf/NonCopyingSort.h @@ -0,0 +1,89 @@ +/* + * Copyright (C) 2010 Apple Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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 APPLE COMPUTER, INC. ``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 APPLE COMPUTER, INC. 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. + * + */ + +#ifndef WTF_NonCopyingSort_h +#define WTF_NonCopyingSort_h + +namespace WTF { + +using std::swap; + +template<typename RandomAccessIterator, typename Predicate> +inline void siftDown(RandomAccessIterator array, ptrdiff_t start, ptrdiff_t end, Predicate compareLess) +{ + ptrdiff_t root = start; + + while (root * 2 + 1 <= end) { + ptrdiff_t child = root * 2 + 1; + if (child < end && compareLess(array[child], array[child + 1])) + child++; + + if (compareLess(array[root], array[child])) { + swap(array[root], array[child]); + root = child; + } else + return; + } +} + +template<typename RandomAccessIterator, typename Predicate> +inline void heapify(RandomAccessIterator array, ptrdiff_t count, Predicate compareLess) +{ + ptrdiff_t start = (count - 2) / 2; + + while (start >= 0) { + siftDown(array, start, count - 1, compareLess); + start--; + } +} + +template<typename RandomAccessIterator, typename Predicate> +void heapSort(RandomAccessIterator start, RandomAccessIterator end, Predicate compareLess) +{ + ptrdiff_t count = end - start; + heapify(start, count, compareLess); + + ptrdiff_t endIndex = count - 1; + while (endIndex > 0) { + swap(start[endIndex], start[0]); + siftDown(start, 0, endIndex - 1, compareLess); + endIndex--; + } +} + +template<typename RandomAccessIterator, typename Predicate> +inline void nonCopyingSort(RandomAccessIterator start, RandomAccessIterator end, Predicate compareLess) +{ + // heapsort happens to use only swaps, not copies, but the essential thing about + // this function is the fact that it does not copy, not the specific algorithm + heapSort(start, end, compareLess); +} + +} // namespace WTF + +using WTF::nonCopyingSort; + +#endif // WTF_NonCopyingSort_h diff --git a/Source/JavaScriptCore/wtf/Noncopyable.h b/Source/JavaScriptCore/wtf/Noncopyable.h new file mode 100644 index 000000000..9f93f173b --- /dev/null +++ b/Source/JavaScriptCore/wtf/Noncopyable.h @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2006, 2010 Apple Inc. All rights reserved. + * + * This library 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 library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#ifndef WTF_Noncopyable_h +#define WTF_Noncopyable_h + +#include "Compiler.h" + +#if COMPILER_SUPPORTS(CXX_DELETED_FUNCTIONS) + #define WTF_MAKE_NONCOPYABLE(ClassName) \ + _Pragma("clang diagnostic push") \ + _Pragma("clang diagnostic ignored \"-Wunknown-pragmas\"") \ + _Pragma("clang diagnostic ignored \"-Wc++0x-extensions\"") \ + private: \ + ClassName(const ClassName&) = delete; \ + ClassName& operator=(const ClassName&) = delete; \ + _Pragma("clang diagnostic pop") +#else + #define WTF_MAKE_NONCOPYABLE(ClassName) \ + private: \ + ClassName(const ClassName&); \ + ClassName& operator=(const ClassName&) +#endif + +#endif // WTF_Noncopyable_h diff --git a/Source/JavaScriptCore/wtf/NotFound.h b/Source/JavaScriptCore/wtf/NotFound.h new file mode 100644 index 000000000..4263bceca --- /dev/null +++ b/Source/JavaScriptCore/wtf/NotFound.h @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2008 Apple Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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 APPLE INC. ``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 APPLE INC. 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. + */ + +#ifndef NotFound_h +#define NotFound_h + +namespace WTF { + + const size_t notFound = static_cast<size_t>(-1); + +} // namespace WTF + +using WTF::notFound; + +#endif // NotFound_h diff --git a/Source/JavaScriptCore/wtf/NullPtr.cpp b/Source/JavaScriptCore/wtf/NullPtr.cpp new file mode 100644 index 000000000..fb75cf6d5 --- /dev/null +++ b/Source/JavaScriptCore/wtf/NullPtr.cpp @@ -0,0 +1,34 @@ +/* + +Copyright (C) 2010 Apple Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. +2. 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 APPLE INC. AND ITS 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 APPLE INC. OR ITS 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 "config.h" +#include "NullPtr.h" + +#if !COMPILER_SUPPORTS(CXX_NULLPTR) + +std::nullptr_t nullptr; + +#endif diff --git a/Source/JavaScriptCore/wtf/NullPtr.h b/Source/JavaScriptCore/wtf/NullPtr.h new file mode 100644 index 000000000..b65f8fab5 --- /dev/null +++ b/Source/JavaScriptCore/wtf/NullPtr.h @@ -0,0 +1,48 @@ +/* + +Copyright (C) 2010 Apple Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. +2. 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 APPLE INC. AND ITS 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 APPLE INC. OR ITS 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. + +*/ + +#ifndef NullPtr_h +#define NullPtr_h + +// For compilers and standard libraries that do not yet include it, this adds the +// nullptr_t type and nullptr object. They are defined in the same namespaces they +// would be in compiler and library that had the support. + +#if COMPILER_SUPPORTS(CXX_NULLPTR) + +#include <cstddef> + +#else + +namespace std { + class nullptr_t { }; +} + +extern std::nullptr_t nullptr; + +#endif + +#endif diff --git a/Source/JavaScriptCore/wtf/OSAllocator.h b/Source/JavaScriptCore/wtf/OSAllocator.h new file mode 100644 index 000000000..bf9f04968 --- /dev/null +++ b/Source/JavaScriptCore/wtf/OSAllocator.h @@ -0,0 +1,100 @@ +/* + * Copyright (C) 2010 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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 APPLE INC. AND ITS 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 APPLE INC. OR ITS 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. + */ + +#ifndef OSAllocator_h +#define OSAllocator_h + +#include <wtf/UnusedParam.h> +#include <wtf/VMTags.h> +#include <wtf/VMTags.h> + +namespace WTF { + +class OSAllocator { +public: + enum Usage { + UnknownUsage = -1, + FastMallocPages = VM_TAG_FOR_TCMALLOC_MEMORY, + JSGCHeapPages = VM_TAG_FOR_COLLECTOR_MEMORY, + JSVMStackPages = VM_TAG_FOR_REGISTERFILE_MEMORY, + JSJITCodePages = VM_TAG_FOR_EXECUTABLEALLOCATOR_MEMORY, + }; + + // These methods are symmetric; reserveUncommitted allocates VM in an uncommitted state, + // releaseDecommitted should be called on a region of VM allocated by a single reservation, + // the memory must all currently be in a decommitted state. + static void* reserveUncommitted(size_t, Usage = UnknownUsage, bool writable = true, bool executable = false, bool includesGuardPages = false); + static void releaseDecommitted(void*, size_t); + + // These methods are symmetric; they commit or decommit a region of VM (uncommitted VM should + // never be accessed, since the OS may not have attached physical memory for these regions). + // Clients should only call commit on uncommitted regions and decommit on committed regions. + static void commit(void*, size_t, bool writable, bool executable); + static void decommit(void*, size_t); + + // These methods are symmetric; reserveAndCommit allocates VM in an committed state, + // decommitAndRelease should be called on a region of VM allocated by a single reservation, + // the memory must all currently be in a committed state. + static void* reserveAndCommit(size_t, Usage = UnknownUsage, bool writable = true, bool executable = false, bool includesGuardPages = false); + static void decommitAndRelease(void* base, size_t size); + + // These methods are akin to reserveAndCommit/decommitAndRelease, above - however rather than + // committing/decommitting the entire region additional parameters allow a subregion to be + // specified. + static void* reserveAndCommit(size_t reserveSize, size_t commitSize, Usage = UnknownUsage, bool writable = true, bool executable = false); + static void decommitAndRelease(void* releaseBase, size_t releaseSize, void* decommitBase, size_t decommitSize); +}; + +inline void* OSAllocator::reserveAndCommit(size_t reserveSize, size_t commitSize, Usage usage, bool writable, bool executable) +{ + void* base = reserveUncommitted(reserveSize, usage, writable, executable); + commit(base, commitSize, writable, executable); + return base; +} + +inline void OSAllocator::decommitAndRelease(void* releaseBase, size_t releaseSize, void* decommitBase, size_t decommitSize) +{ + ASSERT(decommitBase >= releaseBase && (static_cast<char*>(decommitBase) + decommitSize) <= (static_cast<char*>(releaseBase) + releaseSize)); +#if OS(WINCE) + // On most platforms we can actually skip this final decommit; releasing the VM will + // implicitly decommit any physical memory in the region. This is not true on WINCE. + decommit(decommitBase, decommitSize); +#else + UNUSED_PARAM(decommitBase); + UNUSED_PARAM(decommitSize); +#endif + releaseDecommitted(releaseBase, releaseSize); +} + +inline void OSAllocator::decommitAndRelease(void* base, size_t size) +{ + decommitAndRelease(base, size, base, size); +} + +} // namespace WTF + +using WTF::OSAllocator; + +#endif // OSAllocator_h diff --git a/Source/JavaScriptCore/wtf/OSAllocatorPosix.cpp b/Source/JavaScriptCore/wtf/OSAllocatorPosix.cpp new file mode 100644 index 000000000..b264f685a --- /dev/null +++ b/Source/JavaScriptCore/wtf/OSAllocatorPosix.cpp @@ -0,0 +1,153 @@ +/* + * Copyright (C) 2010 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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 APPLE INC. AND ITS 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 APPLE INC. OR ITS 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 "config.h" +#include "OSAllocator.h" + +#include "PageAllocation.h" +#include <errno.h> +#include <sys/mman.h> +#include <wtf/Assertions.h> +#include <wtf/UnusedParam.h> + +namespace WTF { + +void* OSAllocator::reserveUncommitted(size_t bytes, Usage usage, bool writable, bool executable, bool includesGuardPages) +{ + void* result = reserveAndCommit(bytes, usage, writable, executable, includesGuardPages); +#if HAVE(MADV_FREE_REUSE) + // To support the "reserve then commit" model, we have to initially decommit. + while (madvise(result, bytes, MADV_FREE_REUSABLE) == -1 && errno == EAGAIN) { } +#endif + return result; +} + +void* OSAllocator::reserveAndCommit(size_t bytes, Usage usage, bool writable, bool executable, bool includesGuardPages) +{ + // All POSIX reservations start out logically committed. + int protection = PROT_READ; + if (writable) + protection |= PROT_WRITE; + if (executable) + protection |= PROT_EXEC; + + int flags = MAP_PRIVATE | MAP_ANON; +#if PLATFORM(IOS) + if (executable) + flags |= MAP_JIT; +#endif + +#if OS(LINUX) + // Linux distros usually do not allow overcommit by default, so + // JSC's strategy of mmaping a large amount of memory upfront + // won't work very well on some systems. Fortunately there's a + // flag we can pass to mmap to disable the overcommit check for + // this particular call, so we can get away with it as long as the + // overcommit flag value in /proc/sys/vm/overcommit_memory is 0 + // ('heuristic') and not 2 (always check). 0 is the usual default + // value, so this should work well in general. + flags |= MAP_NORESERVE; +#endif + +#if OS(DARWIN) + int fd = usage; +#else + int fd = -1; +#endif + + void* result = 0; +#if (OS(DARWIN) && CPU(X86_64)) + if (executable) { + ASSERT(includesGuardPages); + // Cook up an address to allocate at, using the following recipe: + // 17 bits of zero, stay in userspace kids. + // 26 bits of randomness for ASLR. + // 21 bits of zero, at least stay aligned within one level of the pagetables. + // + // But! - as a temporary workaround for some plugin problems (rdar://problem/6812854), + // for now instead of 2^26 bits of ASLR lets stick with 25 bits of randomization plus + // 2^24, which should put up somewhere in the middle of userspace (in the address range + // 0x200000000000 .. 0x5fffffffffff). + intptr_t randomLocation = 0; + randomLocation = arc4random() & ((1 << 25) - 1); + randomLocation += (1 << 24); + randomLocation <<= 21; + result = reinterpret_cast<void*>(randomLocation); + } +#endif + + result = mmap(result, bytes, protection, flags, fd, 0); + if (result == MAP_FAILED) { + #if ENABLE(INTERPRETER) + if (executable) + result = 0; + else + #endif + CRASH(); + } + if (result && includesGuardPages) { + // We use mmap to remap the guardpages rather than using mprotect as + // mprotect results in multiple references to the code region. This + // breaks the madvise based mechanism we use to return physical memory + // to the OS. + mmap(result, pageSize(), PROT_NONE, MAP_FIXED | MAP_PRIVATE | MAP_ANON, fd, 0); + mmap(static_cast<char*>(result) + bytes - pageSize(), pageSize(), PROT_NONE, MAP_FIXED | MAP_PRIVATE | MAP_ANON, fd, 0); + } + return result; +} + +void OSAllocator::commit(void* address, size_t bytes, bool, bool) +{ +#if HAVE(MADV_FREE_REUSE) + while (madvise(address, bytes, MADV_FREE_REUSE) == -1 && errno == EAGAIN) { } +#else + // Non-MADV_FREE_REUSE reservations automatically commit on demand. + UNUSED_PARAM(address); + UNUSED_PARAM(bytes); +#endif +} + +void OSAllocator::decommit(void* address, size_t bytes) +{ +#if HAVE(MADV_FREE_REUSE) + while (madvise(address, bytes, MADV_FREE_REUSABLE) == -1 && errno == EAGAIN) { } +#elif HAVE(MADV_FREE) + while (madvise(address, bytes, MADV_FREE) == -1 && errno == EAGAIN) { } +#elif HAVE(MADV_DONTNEED) + while (madvise(address, bytes, MADV_DONTNEED) == -1 && errno == EAGAIN) { } +#else + UNUSED_PARAM(address); + UNUSED_PARAM(bytes); +#endif +} + +void OSAllocator::releaseDecommitted(void* address, size_t bytes) +{ + int result = munmap(address, bytes); + if (result == -1) + CRASH(); +} + +} // namespace WTF diff --git a/Source/JavaScriptCore/wtf/OSAllocatorWin.cpp b/Source/JavaScriptCore/wtf/OSAllocatorWin.cpp new file mode 100644 index 000000000..7f5d9b890 --- /dev/null +++ b/Source/JavaScriptCore/wtf/OSAllocatorWin.cpp @@ -0,0 +1,80 @@ +/* + * Copyright (C) 2010 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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 APPLE INC. AND ITS 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 APPLE INC. OR ITS 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 "config.h" +#include "OSAllocator.h" + +#include "windows.h" +#include <wtf/Assertions.h> + +namespace WTF { + +static inline DWORD protection(bool writable, bool executable) +{ + return executable ? + (writable ? PAGE_EXECUTE_READWRITE : PAGE_EXECUTE_READ) : + (writable ? PAGE_READWRITE : PAGE_READONLY); +} + +void* OSAllocator::reserveUncommitted(size_t bytes, Usage, bool writable, bool executable, bool) +{ + void* result = VirtualAlloc(0, bytes, MEM_RESERVE, protection(writable, executable)); + if (!result) + CRASH(); + return result; +} + +void* OSAllocator::reserveAndCommit(size_t bytes, Usage, bool writable, bool executable, bool) +{ + void* result = VirtualAlloc(0, bytes, MEM_RESERVE | MEM_COMMIT, protection(writable, executable)); + if (!result) + CRASH(); + return result; +} + +void OSAllocator::commit(void* address, size_t bytes, bool writable, bool executable) +{ + void* result = VirtualAlloc(address, bytes, MEM_COMMIT, protection(writable, executable)); + if (!result) + CRASH(); +} + +void OSAllocator::decommit(void* address, size_t bytes) +{ + bool result = VirtualFree(address, bytes, MEM_DECOMMIT); + if (!result) + CRASH(); +} + +void OSAllocator::releaseDecommitted(void* address, size_t bytes) +{ + // According to http://msdn.microsoft.com/en-us/library/aa366892(VS.85).aspx, + // dwSize must be 0 if dwFreeType is MEM_RELEASE. + bool result = VirtualFree(address, 0, MEM_RELEASE); + if (!result) + CRASH(); +} + +} // namespace WTF diff --git a/Source/JavaScriptCore/wtf/OSRandomSource.cpp b/Source/JavaScriptCore/wtf/OSRandomSource.cpp new file mode 100644 index 000000000..0c1416a2f --- /dev/null +++ b/Source/JavaScriptCore/wtf/OSRandomSource.cpp @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2011 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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 GOOGLE, INC. ``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 APPLE COMPUTER, INC. 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 "config.h" +#include "OSRandomSource.h" + +#include <stdint.h> +#include <stdlib.h> + +#if OS(UNIX) +#include <fcntl.h> +#include <unistd.h> +#endif + +#if OS(WINDOWS) +#include <windows.h> +#include <wincrypt.h> // windows.h must be included before wincrypt.h. +#endif + +namespace WTF { + +#if USE(OS_RANDOMNESS) +void cryptographicallyRandomValuesFromOS(unsigned char* buffer, size_t length) +{ +#if OS(UNIX) + int fd = open("/dev/urandom", O_RDONLY, 0); + if (fd < 0) + CRASH(); // We need /dev/urandom for this API to work... + + if (read(fd, buffer, length) != static_cast<ssize_t>(length)) + CRASH(); + + close(fd); +#elif OS(WINDOWS) + HCRYPTPROV hCryptProv = 0; + if (!CryptAcquireContext(&hCryptProv, 0, MS_DEF_PROV, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT)) + CRASH(); + if (!CryptGenRandom(hCryptProv, length, buffer)) + CRASH(); + CryptReleaseContext(hCryptProv, 0); +#else + #error "This configuration doesn't have a strong source of randomness." + // WARNING: When adding new sources of OS randomness, the randomness must + // be of cryptographic quality! +#endif +} +#endif + +} diff --git a/Source/JavaScriptCore/wtf/OSRandomSource.h b/Source/JavaScriptCore/wtf/OSRandomSource.h new file mode 100644 index 000000000..214a95472 --- /dev/null +++ b/Source/JavaScriptCore/wtf/OSRandomSource.h @@ -0,0 +1,41 @@ +/* + * Copyright (C) Google, Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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 GOOGLE, INC. ``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 APPLE COMPUTER, INC. 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. + */ + +#ifndef WTF_OSRandomSource_h +#define WTF_OSRandomSource_h + +namespace WTF { + +#if USE(OS_RANDOMNESS) +// This function attempts to fill buffer with randomness from the operating +// system. If insufficient randomness is available, the buffer will be +// partially filled. Rather than calling this function directly, consider +// calling cryptographicallyRandomNumber or cryptographicallyRandomValues. +void cryptographicallyRandomValuesFromOS(unsigned char* buffer, size_t length); +#endif + +} + +#endif diff --git a/Source/JavaScriptCore/wtf/OwnArrayPtr.h b/Source/JavaScriptCore/wtf/OwnArrayPtr.h new file mode 100644 index 000000000..0fc8e71b3 --- /dev/null +++ b/Source/JavaScriptCore/wtf/OwnArrayPtr.h @@ -0,0 +1,157 @@ +/* + * Copyright (C) 2006, 2010 Apple Inc. All rights reserved. + * + * This library 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 library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#ifndef WTF_OwnArrayPtr_h +#define WTF_OwnArrayPtr_h + +#include "Assertions.h" +#include "Noncopyable.h" +#include "NullPtr.h" +#include "PassOwnArrayPtr.h" +#include <algorithm> + +namespace WTF { + +template<typename T> class PassOwnArrayPtr; +template<typename T> PassOwnArrayPtr<T> adoptArrayPtr(T*); + +template <typename T> class OwnArrayPtr { +public: + typedef T* PtrType; + + OwnArrayPtr() : m_ptr(0) { } + + // See comment in PassOwnArrayPtr.h for why this takes a const reference. + template<typename U> OwnArrayPtr(const PassOwnArrayPtr<U>& o); + + // This copy constructor is used implicitly by gcc when it generates + // transients for assigning a PassOwnArrayPtr<T> object to a stack-allocated + // OwnArrayPtr<T> object. It should never be called explicitly and gcc + // should optimize away the constructor when generating code. + OwnArrayPtr(const OwnArrayPtr<T>&); + + ~OwnArrayPtr() { deleteOwnedArrayPtr(m_ptr); } + + PtrType get() const { return m_ptr; } + + void clear(); + PassOwnArrayPtr<T> release(); + PtrType leakPtr() WARN_UNUSED_RETURN; + + T& operator*() const { ASSERT(m_ptr); return *m_ptr; } + PtrType operator->() const { ASSERT(m_ptr); return m_ptr; } + + T& operator[](std::ptrdiff_t i) const { ASSERT(m_ptr); ASSERT(i >= 0); return m_ptr[i]; } + + bool operator!() const { return !m_ptr; } + + // This conversion operator allows implicit conversion to bool but not to other integer types. + typedef T* OwnArrayPtr::*UnspecifiedBoolType; + operator UnspecifiedBoolType() const { return m_ptr ? &OwnArrayPtr::m_ptr : 0; } + + OwnArrayPtr& operator=(const PassOwnArrayPtr<T>&); + OwnArrayPtr& operator=(std::nullptr_t) { clear(); return *this; } + template<typename U> OwnArrayPtr& operator=(const PassOwnArrayPtr<U>&); + + void swap(OwnArrayPtr& o) { std::swap(m_ptr, o.m_ptr); } + +private: + PtrType m_ptr; +}; + +template<typename T> template<typename U> inline OwnArrayPtr<T>::OwnArrayPtr(const PassOwnArrayPtr<U>& o) + : m_ptr(o.leakPtr()) +{ +} + +template<typename T> inline void OwnArrayPtr<T>::clear() +{ + PtrType ptr = m_ptr; + m_ptr = 0; + deleteOwnedArrayPtr(ptr); +} + +template<typename T> inline PassOwnArrayPtr<T> OwnArrayPtr<T>::release() +{ + PtrType ptr = m_ptr; + m_ptr = 0; + return adoptArrayPtr(ptr); +} + +template<typename T> inline typename OwnArrayPtr<T>::PtrType OwnArrayPtr<T>::leakPtr() +{ + PtrType ptr = m_ptr; + m_ptr = 0; + return ptr; +} + +template<typename T> inline OwnArrayPtr<T>& OwnArrayPtr<T>::operator=(const PassOwnArrayPtr<T>& o) +{ + PtrType ptr = m_ptr; + m_ptr = o.leakPtr(); + ASSERT(!ptr || m_ptr != ptr); + deleteOwnedArrayPtr(ptr); + return *this; +} + +template<typename T> template<typename U> inline OwnArrayPtr<T>& OwnArrayPtr<T>::operator=(const PassOwnArrayPtr<U>& o) +{ + PtrType ptr = m_ptr; + m_ptr = o.leakPtr(); + ASSERT(!ptr || m_ptr != ptr); + deleteOwnedArrayPtr(ptr); + return *this; +} + +template <typename T> inline void swap(OwnArrayPtr<T>& a, OwnArrayPtr<T>& b) +{ + a.swap(b); +} + +template<typename T, typename U> inline bool operator==(const OwnArrayPtr<T>& a, U* b) +{ + return a.get() == b; +} + +template<typename T, typename U> inline bool operator==(T* a, const OwnArrayPtr<U>& b) +{ + return a == b.get(); +} + +template<typename T, typename U> inline bool operator!=(const OwnArrayPtr<T>& a, U* b) +{ + return a.get() != b; +} + +template<typename T, typename U> inline bool operator!=(T* a, const OwnArrayPtr<U>& b) +{ + return a != b.get(); +} + +template <typename T> inline T* getPtr(const OwnArrayPtr<T>& p) +{ + return p.get(); +} + +} // namespace WTF + +using WTF::OwnArrayPtr; + +#endif // WTF_OwnArrayPtr_h diff --git a/Source/JavaScriptCore/wtf/OwnFastMallocPtr.h b/Source/JavaScriptCore/wtf/OwnFastMallocPtr.h new file mode 100644 index 000000000..183bfec8f --- /dev/null +++ b/Source/JavaScriptCore/wtf/OwnFastMallocPtr.h @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2006, 2007 Apple Inc. All rights reserved. + * Copyright (C) 2009 Google Inc. All rights reserved. + * + * This library 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 library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#ifndef OwnFastMallocPtr_h +#define OwnFastMallocPtr_h + +#include "FastMalloc.h" +#include <wtf/Noncopyable.h> + +namespace WTF { + + template<class T> class OwnFastMallocPtr { + WTF_MAKE_NONCOPYABLE(OwnFastMallocPtr); + public: + explicit OwnFastMallocPtr(T* ptr) : m_ptr(ptr) + { + } + + ~OwnFastMallocPtr() + { + fastFree(const_cast<void*>(static_cast<const void*>(const_cast<const T*>(m_ptr)))); + } + + T* get() const { return m_ptr; } + T* release() { T* ptr = m_ptr; m_ptr = 0; return ptr; } + + private: + T* m_ptr; + }; + +} // namespace WTF + +using WTF::OwnFastMallocPtr; + +#endif // OwnFastMallocPtr_h diff --git a/Source/JavaScriptCore/wtf/OwnPtr.h b/Source/JavaScriptCore/wtf/OwnPtr.h new file mode 100644 index 000000000..097967964 --- /dev/null +++ b/Source/JavaScriptCore/wtf/OwnPtr.h @@ -0,0 +1,169 @@ +/* + * Copyright (C) 2006, 2007, 2008, 2009, 2010 Apple Inc. All rights reserved. + * + * This library 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 library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#ifndef WTF_OwnPtr_h +#define WTF_OwnPtr_h + +#include "Assertions.h" +#include "NullPtr.h" +#include "OwnPtrCommon.h" +#include "TypeTraits.h" +#include <algorithm> +#include <memory> + +namespace WTF { + + // Unlike most of our smart pointers, OwnPtr can take either the pointer type or the pointed-to type. + + template<typename T> class PassOwnPtr; + template<typename T> PassOwnPtr<T> adoptPtr(T*); + + template<typename T> class OwnPtr { + public: + typedef typename RemovePointer<T>::Type ValueType; + typedef ValueType* PtrType; + + OwnPtr() : m_ptr(0) { } + OwnPtr(std::nullptr_t) : m_ptr(0) { } + + // See comment in PassOwnPtr.h for why this takes a const reference. + template<typename U> OwnPtr(const PassOwnPtr<U>& o); + + // This copy constructor is used implicitly by gcc when it generates + // transients for assigning a PassOwnPtr<T> object to a stack-allocated + // OwnPtr<T> object. It should never be called explicitly and gcc + // should optimize away the constructor when generating code. + OwnPtr(const OwnPtr<ValueType>&); + + ~OwnPtr() { deleteOwnedPtr(m_ptr); } + + PtrType get() const { return m_ptr; } + + void clear(); + PassOwnPtr<T> release(); + PtrType leakPtr() WARN_UNUSED_RETURN; + + ValueType& operator*() const { ASSERT(m_ptr); return *m_ptr; } + PtrType operator->() const { ASSERT(m_ptr); return m_ptr; } + + bool operator!() const { return !m_ptr; } + + // This conversion operator allows implicit conversion to bool but not to other integer types. + typedef PtrType OwnPtr::*UnspecifiedBoolType; + operator UnspecifiedBoolType() const { return m_ptr ? &OwnPtr::m_ptr : 0; } + + OwnPtr& operator=(const PassOwnPtr<T>&); + OwnPtr& operator=(std::nullptr_t) { clear(); return *this; } + template<typename U> OwnPtr& operator=(const PassOwnPtr<U>&); + + void swap(OwnPtr& o) { std::swap(m_ptr, o.m_ptr); } + + private: + OwnPtr& operator=(const OwnPtr<T>&); + + // We should never have two OwnPtrs for the same underlying object (otherwise we'll get + // double-destruction), so these equality operators should never be needed. + template<typename U> bool operator==(const OwnPtr<U>&) { COMPILE_ASSERT(!sizeof(U*), OwnPtrs_should_never_be_equal); return false; } + template<typename U> bool operator!=(const OwnPtr<U>&) { COMPILE_ASSERT(!sizeof(U*), OwnPtrs_should_never_be_equal); return false; } + template<typename U> bool operator==(const PassOwnPtr<U>&) { COMPILE_ASSERT(!sizeof(U*), OwnPtrs_should_never_be_equal); return false; } + template<typename U> bool operator!=(const PassOwnPtr<U>&) { COMPILE_ASSERT(!sizeof(U*), OwnPtrs_should_never_be_equal); return false; } + + PtrType m_ptr; + }; + + template<typename T> template<typename U> inline OwnPtr<T>::OwnPtr(const PassOwnPtr<U>& o) + : m_ptr(o.leakPtr()) + { + } + + template<typename T> inline void OwnPtr<T>::clear() + { + PtrType ptr = m_ptr; + m_ptr = 0; + deleteOwnedPtr(ptr); + } + + template<typename T> inline PassOwnPtr<T> OwnPtr<T>::release() + { + PtrType ptr = m_ptr; + m_ptr = 0; + return adoptPtr(ptr); + } + + template<typename T> inline typename OwnPtr<T>::PtrType OwnPtr<T>::leakPtr() + { + PtrType ptr = m_ptr; + m_ptr = 0; + return ptr; + } + + template<typename T> inline OwnPtr<T>& OwnPtr<T>::operator=(const PassOwnPtr<T>& o) + { + PtrType ptr = m_ptr; + m_ptr = o.leakPtr(); + ASSERT(!ptr || m_ptr != ptr); + deleteOwnedPtr(ptr); + return *this; + } + + template<typename T> template<typename U> inline OwnPtr<T>& OwnPtr<T>::operator=(const PassOwnPtr<U>& o) + { + PtrType ptr = m_ptr; + m_ptr = o.leakPtr(); + ASSERT(!ptr || m_ptr != ptr); + deleteOwnedPtr(ptr); + return *this; + } + + template<typename T> inline void swap(OwnPtr<T>& a, OwnPtr<T>& b) + { + a.swap(b); + } + + template<typename T, typename U> inline bool operator==(const OwnPtr<T>& a, U* b) + { + return a.get() == b; + } + + template<typename T, typename U> inline bool operator==(T* a, const OwnPtr<U>& b) + { + return a == b.get(); + } + + template<typename T, typename U> inline bool operator!=(const OwnPtr<T>& a, U* b) + { + return a.get() != b; + } + + template<typename T, typename U> inline bool operator!=(T* a, const OwnPtr<U>& b) + { + return a != b.get(); + } + + template<typename T> inline typename OwnPtr<T>::PtrType getPtr(const OwnPtr<T>& p) + { + return p.get(); + } + +} // namespace WTF + +using WTF::OwnPtr; + +#endif // WTF_OwnPtr_h diff --git a/Source/JavaScriptCore/wtf/OwnPtrCommon.h b/Source/JavaScriptCore/wtf/OwnPtrCommon.h new file mode 100644 index 000000000..16283aed2 --- /dev/null +++ b/Source/JavaScriptCore/wtf/OwnPtrCommon.h @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2009 Apple Inc. All rights reserved. + * Copyright (C) 2009 Torch Mobile, Inc. + * Copyright (C) 2010 Company 100 Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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 APPLE INC. ``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 APPLE INC. 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. + */ + +#ifndef WTF_OwnPtrCommon_h +#define WTF_OwnPtrCommon_h + +#if PLATFORM(WIN) +typedef struct HBITMAP__* HBITMAP; +typedef struct HBRUSH__* HBRUSH; +typedef struct HDC__* HDC; +typedef struct HFONT__* HFONT; +typedef struct HPALETTE__* HPALETTE; +typedef struct HPEN__* HPEN; +typedef struct HRGN__* HRGN; +#endif + +#if PLATFORM(EFL) +typedef struct _Ecore_Evas Ecore_Evas; +typedef struct _Ecore_Pipe Ecore_Pipe; +typedef struct _Evas_Object Evas_Object; +#endif + +namespace WTF { + + template <typename T> inline void deleteOwnedPtr(T* ptr) + { + typedef char known[sizeof(T) ? 1 : -1]; + if (sizeof(known)) + delete ptr; + } + +#if PLATFORM(WIN) + void deleteOwnedPtr(HBITMAP); + void deleteOwnedPtr(HBRUSH); + void deleteOwnedPtr(HDC); + void deleteOwnedPtr(HFONT); + void deleteOwnedPtr(HPALETTE); + void deleteOwnedPtr(HPEN); + void deleteOwnedPtr(HRGN); +#endif + +#if PLATFORM(EFL) + void deleteOwnedPtr(Ecore_Evas*); + void deleteOwnedPtr(Ecore_Pipe*); + void deleteOwnedPtr(Evas_Object*); +#endif + +} // namespace WTF + +#endif // WTF_OwnPtrCommon_h diff --git a/Source/JavaScriptCore/wtf/PackedIntVector.h b/Source/JavaScriptCore/wtf/PackedIntVector.h new file mode 100644 index 000000000..76fa48a1d --- /dev/null +++ b/Source/JavaScriptCore/wtf/PackedIntVector.h @@ -0,0 +1,126 @@ +/* + * Copyright (C) 2011 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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 APPLE INC. ``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 APPLE INC. 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. + */ + +#ifndef PackedIntVector_h +#define PackedIntVector_h + +#include "BitVector.h" + +namespace WTF { + +// This class allows you to create an array of integers, where those +// integers have only a handful of bits each. It is not meant to be +// efficient in time, but only in space. (Though making it efficient +// in time for power-of-2 values of bitCount would not be difficult.) +// Note that this does not work as expected for signed types, if you +// are relying on the sign being preserved. + +template<typename T, unsigned bitCount> +class PackedIntVector { +public: + PackedIntVector() + { + ASSERT(bitCount); + ASSERT(bitCount < sizeof(void*) * 8); + } + + PackedIntVector(const PackedIntVector& other) + : m_bits(other.m_bits) + { + } + + PackedIntVector& operator=(const PackedIntVector& other) + { + m_bits = other.m_bits; + return *this; + } + + size_t size() const + { + return m_bits.size() / bitCount; + } + + void ensureSize(size_t numInts) + { + m_bits.ensureSize(numInts * bitCount); + } + + void resize(size_t numInts) + { + m_bits.resize(numInts * bitCount); + } + + void clearAll() + { + m_bits.clearAll(); + } + + T get(size_t index) const + { + uintptr_t result = 0; + for (unsigned subIndex = 0; subIndex < bitCount; ++subIndex) { + result <<= 1; + result |= (m_bits.quickGet(index * bitCount + subIndex) ? 1 : 0); + } + return static_cast<T>(result); + } + + void set(size_t index, T value) + { + // Do arithmetic using uintptr_t, because (1) we know what it is + // (T might be an enum) and (2) it's the largest integer type that + // is likely to perform decently well. + uintptr_t myValue = static_cast<uintptr_t>(value); + + // Preliminary sanity check that the value is not out of range. + ASSERT((myValue & mask()) == myValue); + + for (unsigned subIndex = bitCount; subIndex-- > 0;) { + m_bits.quickSet(index * bitCount + subIndex, !!(myValue & 1)); + myValue >>= 1; + } + + // Final sanity check that we stored what the user thought we + // stored. + ASSERT(get(index) == value); + } +private: + // This returns the mask, and is careful to not step on the wrap-around + // semantics of the shift amount (1 << 32 is 1 since 32 wraps to 0). There + // is the separate question of why you would ever use this to store 32-bit + // or 64-bit values, but it's probably better to have this work as expected + // in such situations regardless. + static uintptr_t mask() { return (static_cast<uintptr_t>(2) << (bitCount - 1)) - 1; } + + // Stores integers bit by bit in big endian. + BitVector m_bits; +}; + +} // namespace WTF + +using WTF::PackedIntVector; + +#endif // PackedIntVector_h + diff --git a/Source/JavaScriptCore/wtf/PageAllocation.h b/Source/JavaScriptCore/wtf/PageAllocation.h new file mode 100644 index 000000000..18d31880c --- /dev/null +++ b/Source/JavaScriptCore/wtf/PageAllocation.h @@ -0,0 +1,120 @@ +/* + * Copyright (C) 2010 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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 APPLE INC. ``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 APPLE INC. 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. + */ + +#ifndef PageAllocation_h +#define PageAllocation_h + +#include <wtf/Assertions.h> +#include <wtf/OSAllocator.h> +#include <wtf/PageBlock.h> +#include <wtf/UnusedParam.h> +#include <wtf/VMTags.h> +#include <algorithm> + +#if OS(DARWIN) +#include <mach/mach_init.h> +#include <mach/vm_map.h> +#endif + +#if OS(WINDOWS) +#include <malloc.h> +#include <windows.h> +#endif + +#if HAVE(ERRNO_H) +#include <errno.h> +#endif + +#if HAVE(MMAP) +#include <sys/mman.h> +#include <unistd.h> +#endif + +namespace WTF { + +/* + PageAllocation + + The PageAllocation class provides a cross-platform memory allocation interface + with similar capabilities to posix mmap/munmap. Memory is allocated by calling + PageAllocation::allocate, and deallocated by calling deallocate on the + PageAllocation object. The PageAllocation holds the allocation's base pointer + and size. + + The allocate method is passed the size required (which must be a multiple of + the system page size, which can be accessed using PageAllocation::pageSize). + Callers may also optinally provide a flag indicating the usage (for use by + system memory usage tracking tools, where implemented), and boolean values + specifying the required protection (defaulting to writable, non-executable). +*/ + +class PageAllocation : private PageBlock { +public: + PageAllocation() + { + } + + using PageBlock::size; + using PageBlock::base; + +#ifndef __clang__ + using PageBlock::operator bool; +#else + // FIXME: This is a workaround for <rdar://problem/8876150>, wherein Clang incorrectly emits an access + // control warning when a client tries to use operator bool exposed above via "using PageBlock::operator bool". + operator bool() const { return PageBlock::operator bool(); } +#endif + + static PageAllocation allocate(size_t size, OSAllocator::Usage usage = OSAllocator::UnknownUsage, bool writable = true, bool executable = false) + { + ASSERT(isPageAligned(size)); + return PageAllocation(OSAllocator::reserveAndCommit(size, usage, writable, executable), size); + } + + void deallocate() + { + // Clear base & size before calling release; if this is *inside* allocation + // then we won't be able to clear then after deallocating the memory. + PageAllocation tmp; + std::swap(tmp, *this); + + ASSERT(tmp); + ASSERT(!*this); + + OSAllocator::decommitAndRelease(tmp.base(), tmp.size()); + } + +private: + PageAllocation(void* base, size_t size) + : PageBlock(base, size, false) + { + } +}; + +} // namespace WTF + +using WTF::PageAllocation; + +#endif // PageAllocation_h diff --git a/Source/JavaScriptCore/wtf/PageAllocationAligned.cpp b/Source/JavaScriptCore/wtf/PageAllocationAligned.cpp new file mode 100644 index 000000000..6f54710d0 --- /dev/null +++ b/Source/JavaScriptCore/wtf/PageAllocationAligned.cpp @@ -0,0 +1,87 @@ +/* + * Copyright (C) 2010 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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 APPLE INC. AND ITS 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 APPLE INC. OR ITS 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 "config.h" +#include "PageAllocationAligned.h" + +namespace WTF { + +PageAllocationAligned PageAllocationAligned::allocate(size_t size, size_t alignment, OSAllocator::Usage usage, bool writable, bool executable) +{ + ASSERT(isPageAligned(size)); + ASSERT(isPageAligned(alignment)); + ASSERT(isPowerOfTwo(alignment)); + ASSERT(size >= alignment); + size_t alignmentMask = alignment - 1; + +#if OS(DARWIN) + int flags = VM_FLAGS_ANYWHERE; + if (usage != OSAllocator::UnknownUsage) + flags |= usage; + int protection = PROT_READ; + if (writable) + protection |= PROT_WRITE; + if (executable) + protection |= PROT_EXEC; + + vm_address_t address = 0; + vm_map(current_task(), &address, size, alignmentMask, flags, MEMORY_OBJECT_NULL, 0, FALSE, protection, PROT_READ | PROT_WRITE | PROT_EXEC, VM_INHERIT_DEFAULT); + return PageAllocationAligned(reinterpret_cast<void*>(address), size); +#else + size_t alignmentDelta = alignment - pageSize(); + + // Resererve with suffcient additional VM to correctly align. + size_t reservationSize = size + alignmentDelta; + void* reservationBase = OSAllocator::reserveUncommitted(reservationSize, usage, writable, executable); + + // Select an aligned region within the reservation and commit. + void* alignedBase = reinterpret_cast<uintptr_t>(reservationBase) & alignmentMask + ? reinterpret_cast<void*>((reinterpret_cast<uintptr_t>(reservationBase) & ~alignmentMask) + alignment) + : reservationBase; + OSAllocator::commit(alignedBase, size, writable, executable); + + return PageAllocationAligned(alignedBase, size, reservationBase, reservationSize); +#endif +} + +void PageAllocationAligned::deallocate() +{ + // Clear base & size before calling release; if this is *inside* allocation + // then we won't be able to clear then after deallocating the memory. + PageAllocationAligned tmp; + std::swap(tmp, *this); + + ASSERT(tmp); + ASSERT(!*this); + +#if OS(DARWIN) + vm_deallocate(current_task(), reinterpret_cast<vm_address_t>(tmp.base()), tmp.size()); +#else + ASSERT(tmp.m_reservation.contains(tmp.base(), tmp.size())); + OSAllocator::decommitAndRelease(tmp.m_reservation.base(), tmp.m_reservation.size(), tmp.base(), tmp.size()); +#endif +} + +} // namespace WTF diff --git a/Source/JavaScriptCore/wtf/PageAllocationAligned.h b/Source/JavaScriptCore/wtf/PageAllocationAligned.h new file mode 100644 index 000000000..c018dabd8 --- /dev/null +++ b/Source/JavaScriptCore/wtf/PageAllocationAligned.h @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2010 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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 APPLE INC. AND ITS 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 APPLE INC. OR ITS 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. + */ + +#ifndef PageAllocationAligned_h +#define PageAllocationAligned_h + +#include <wtf/OSAllocator.h> +#include <wtf/PageReservation.h> + +namespace WTF { + +class PageAllocationAligned : private PageBlock { +public: + PageAllocationAligned() + { + } + + using PageBlock::operator bool; + using PageBlock::size; + using PageBlock::base; + + static PageAllocationAligned allocate(size_t size, size_t alignment, OSAllocator::Usage usage = OSAllocator::UnknownUsage, bool writable = true, bool executable = false); + + void deallocate(); + +private: +#if OS(DARWIN) + PageAllocationAligned(void* base, size_t size) + : PageBlock(base, size, false) + { + } +#else + PageAllocationAligned(void* base, size_t size, void* reservationBase, size_t reservationSize) + : PageBlock(base, size, false) + , m_reservation(reservationBase, reservationSize, false) + { + } + + PageBlock m_reservation; +#endif +}; + + +} // namespace WTF + +using WTF::PageAllocationAligned; + +#endif // PageAllocationAligned_h diff --git a/Source/JavaScriptCore/wtf/PageBlock.cpp b/Source/JavaScriptCore/wtf/PageBlock.cpp new file mode 100644 index 000000000..f7c546356 --- /dev/null +++ b/Source/JavaScriptCore/wtf/PageBlock.cpp @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2010 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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 APPLE INC. AND ITS 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 APPLE INC. OR ITS 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 "config.h" +#include "PageBlock.h" + +#if OS(UNIX) +#include <unistd.h> +#endif + +#if OS(WINDOWS) +#include <malloc.h> +#include <windows.h> +#endif + +namespace WTF { + +static size_t s_pageSize; + +#if OS(UNIX) + +inline size_t systemPageSize() +{ + return getpagesize(); +} + +#elif OS(WINDOWS) + +inline size_t systemPageSize() +{ + static size_t size = 0; + SYSTEM_INFO system_info; + GetSystemInfo(&system_info); + size = system_info.dwPageSize; + return size; +} + +#endif + +size_t pageSize() +{ + if (!s_pageSize) + s_pageSize = systemPageSize(); + ASSERT(isPowerOfTwo(s_pageSize)); + return s_pageSize; +} + +} // namespace WTF diff --git a/Source/JavaScriptCore/wtf/PageBlock.h b/Source/JavaScriptCore/wtf/PageBlock.h new file mode 100644 index 000000000..e17c5460a --- /dev/null +++ b/Source/JavaScriptCore/wtf/PageBlock.h @@ -0,0 +1,87 @@ +/* + * Copyright (C) 2010 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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 APPLE INC. AND ITS 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 APPLE INC. OR ITS 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. + */ + +#ifndef PageBlock_h +#define PageBlock_h + +namespace WTF { + +size_t pageSize(); +inline bool isPageAligned(void* address) { return !(reinterpret_cast<intptr_t>(address) & (pageSize() - 1)); } +inline bool isPageAligned(size_t size) { return !(size & (pageSize() - 1)); } +inline bool isPowerOfTwo(size_t size) { return !(size & (size - 1)); } + +class PageBlock { +public: + PageBlock(); + PageBlock(const PageBlock&); + PageBlock(void*, size_t, bool hasGuardPages); + + void* base() const { return m_base; } + size_t size() const { return m_size; } + + operator bool() const { return !!m_realBase; } + + bool contains(void* containedBase, size_t containedSize) + { + return containedBase >= m_base + && (static_cast<char*>(containedBase) + containedSize) <= (static_cast<char*>(m_base) + m_size); + } + +private: + void* m_realBase; + void* m_base; + size_t m_size; +}; + +inline PageBlock::PageBlock() + : m_realBase(0) + , m_base(0) + , m_size(0) +{ +} + +inline PageBlock::PageBlock(const PageBlock& other) + : m_realBase(other.m_realBase) + , m_base(other.m_base) + , m_size(other.m_size) +{ +} + +inline PageBlock::PageBlock(void* base, size_t size, bool hasGuardPages) + : m_realBase(base) + , m_base(static_cast<char*>(base) + ((base && hasGuardPages) ? pageSize() : 0)) + , m_size(size) +{ +} + +} // namespace WTF + +using WTF::pageSize; +using WTF::isPageAligned; +using WTF::isPageAligned; +using WTF::isPowerOfTwo; + +#endif // PageBlock_h diff --git a/Source/JavaScriptCore/wtf/PageReservation.h b/Source/JavaScriptCore/wtf/PageReservation.h new file mode 100644 index 000000000..77783ebcc --- /dev/null +++ b/Source/JavaScriptCore/wtf/PageReservation.h @@ -0,0 +1,149 @@ +/* + * Copyright (C) 2010 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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 APPLE INC. ``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 APPLE INC. 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. + */ + +#ifndef PageReservation_h +#define PageReservation_h + +#include <wtf/PageAllocation.h> + +namespace WTF { + +/* + PageReservation + + Like PageAllocation, the PageReservation class provides a cross-platform memory + allocation interface, but with a set of capabilities more similar to that of + VirtualAlloc than posix mmap. PageReservation can be used to allocate virtual + memory without committing physical memory pages using PageReservation::reserve. + Following a call to reserve all memory in the region is in a decommited state, + in which the memory should not be used (accessing the memory may cause a fault). + + Before using memory it must be committed by calling commit, which is passed start + and size values (both of which require system page size granularity). One the + committed memory is no longer needed 'decommit' may be called to return the + memory to its devommitted state. Commit should only be called on memory that is + currently decommitted, and decommit should only be called on memory regions that + are currently committed. All memory should be decommited before the reservation + is deallocated. Values in memory may not be retained accross a pair of calls if + the region of memory is decommitted and then committed again. + + Memory protection should not be changed on decommitted memory, and if protection + is changed on memory while it is committed it should be returned to the orignal + protection before decommit is called. +*/ + +class PageReservation : private PageBlock { +public: + PageReservation() + : m_committed(0) + , m_writable(false) + , m_executable(false) + { + } + + using PageBlock::base; + using PageBlock::size; + +#ifndef __clang__ + using PageBlock::operator bool; +#else + // FIXME: This is a workaround for <rdar://problem/8876150>, wherein Clang incorrectly emits an access + // control warning when a client tries to use operator bool exposed above via "using PageBlock::operator bool". + operator bool() const { return PageBlock::operator bool(); } +#endif + + void commit(void* start, size_t size) + { + ASSERT(*this); + ASSERT(isPageAligned(start)); + ASSERT(isPageAligned(size)); + ASSERT(contains(start, size)); + + m_committed += size; + OSAllocator::commit(start, size, m_writable, m_executable); + } + + void decommit(void* start, size_t size) + { + ASSERT(*this); + ASSERT(isPageAligned(start)); + ASSERT(isPageAligned(size)); + ASSERT(contains(start, size)); + + m_committed -= size; + OSAllocator::decommit(start, size); + } + + size_t committed() + { + return m_committed; + } + + static PageReservation reserve(size_t size, OSAllocator::Usage usage = OSAllocator::UnknownUsage, bool writable = true, bool executable = false) + { + ASSERT(isPageAligned(size)); + return PageReservation(OSAllocator::reserveUncommitted(size, usage, writable, executable), size, writable, executable, false); + } + + static PageReservation reserveWithGuardPages(size_t size, OSAllocator::Usage usage = OSAllocator::UnknownUsage, bool writable = true, bool executable = false) + { + ASSERT(isPageAligned(size)); + return PageReservation(OSAllocator::reserveUncommitted(size + pageSize() * 2, usage, writable, executable, true), size, writable, executable, true); + } + + void deallocate() + { + ASSERT(!m_committed); + + // Clear base & size before calling release; if this is *inside* allocation + // then we won't be able to clear then after deallocating the memory. + PageReservation tmp; + std::swap(tmp, *this); + + ASSERT(tmp); + ASSERT(!*this); + + OSAllocator::releaseDecommitted(tmp.base(), tmp.size()); + } + +private: + PageReservation(void* base, size_t size, bool writable, bool executable, bool hasGuardPages) + : PageBlock(base, size, hasGuardPages) + , m_committed(0) + , m_writable(writable) + , m_executable(executable) + { + } + + size_t m_committed; + bool m_writable; + bool m_executable; +}; + +} + +using WTF::PageReservation; + +#endif // PageReservation_h diff --git a/Source/JavaScriptCore/wtf/ParallelJobs.h b/Source/JavaScriptCore/wtf/ParallelJobs.h new file mode 100644 index 000000000..92b9c5b5b --- /dev/null +++ b/Source/JavaScriptCore/wtf/ParallelJobs.h @@ -0,0 +1,105 @@ +/* + * Copyright (C) 2011 University of Szeged + * Copyright (C) 2011 Gabor Loki <loki@webkit.org> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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 UNIVERSITY OF SZEGED ``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 UNIVERSITY OF SZEGED 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. + */ + +#ifndef ParallelJobs_h +#define ParallelJobs_h + +#include "Assertions.h" +#include "Noncopyable.h" +#include "RefPtr.h" +#include <wtf/Vector.h> + +// Usage: +// +// // Initialize parallel jobs +// ParallelJobs<TypeOfParameter> parallelJobs(&worker [, requestedNumberOfJobs]); +// +// // Fill the parameter array +// for(i = 0; i < parallelJobs.numberOfJobs(); ++i) { +// TypeOfParameter& params = parallelJobs.parameter(i); +// params.attr1 = localVars ... +// ... +// } +// +// // Execute parallel jobs +// parallelJobs.execute(); +// + +#if ENABLE(THREADING_GENERIC) +#include "ParallelJobsGeneric.h" + +#elif ENABLE(THREADING_OPENMP) +#include "ParallelJobsOpenMP.h" + +#elif ENABLE(THREADING_LIBDISPATCH) +#include "ParallelJobsLibdispatch.h" + +#else +#error "No parallel processing API for ParallelJobs" + +#endif + +namespace WTF { + +template<typename Type> +class ParallelJobs { + WTF_MAKE_FAST_ALLOCATED; +public: + typedef void (*WorkerFunction)(Type*); + + ParallelJobs(WorkerFunction func, int requestedJobNumber) : + m_parallelEnvironment(reinterpret_cast<ParallelEnvironment::ThreadFunction>(func), sizeof(Type), requestedJobNumber) + { + m_parameters.grow(m_parallelEnvironment.numberOfJobs()); + ASSERT(numberOfJobs() == m_parameters.size()); + } + + size_t numberOfJobs() + { + return m_parameters.size(); + } + + Type& parameter(size_t i) + { + return m_parameters[i]; + } + + void execute() + { + m_parallelEnvironment.execute(reinterpret_cast<unsigned char*>(m_parameters.data())); + } + +private: + ParallelEnvironment m_parallelEnvironment; + Vector<Type> m_parameters; +}; + +} // namespace WTF + +using WTF::ParallelJobs; + +#endif // ParallelJobs_h diff --git a/Source/JavaScriptCore/wtf/ParallelJobsGeneric.cpp b/Source/JavaScriptCore/wtf/ParallelJobsGeneric.cpp new file mode 100644 index 000000000..b6207dc88 --- /dev/null +++ b/Source/JavaScriptCore/wtf/ParallelJobsGeneric.cpp @@ -0,0 +1,181 @@ +/* + * Copyright (C) 2011 University of Szeged + * Copyright (C) 2011 Gabor Loki <loki@webkit.org> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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 UNIVERSITY OF SZEGED ``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 UNIVERSITY OF SZEGED 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 "config.h" + +#if ENABLE(THREADING_GENERIC) + +#include "ParallelJobs.h" +#include "UnusedParam.h" + +#if OS(DARWIN) || OS(OPENBSD) || OS(NETBSD) +#include <sys/sysctl.h> +#include <sys/types.h> +#elif OS(LINUX) || OS(AIX) || OS(SOLARIS) +#include <unistd.h> +#elif OS(WINDOWS) +#include <windows.h> +#endif + +namespace WTF { + +Vector< RefPtr<ParallelEnvironment::ThreadPrivate> >* ParallelEnvironment::s_threadPool = 0; + +int ParallelEnvironment::s_maxNumberOfParallelThreads = -1; + +ParallelEnvironment::ParallelEnvironment(ThreadFunction threadFunction, size_t sizeOfParameter, int requestedJobNumber) : + m_threadFunction(threadFunction), + m_sizeOfParameter(sizeOfParameter) +{ + ASSERT_ARG(requestedJobNumber, requestedJobNumber >= 1); + + if (s_maxNumberOfParallelThreads == -1) + determineMaxNumberOfParallelThreads(); + + if (!requestedJobNumber || requestedJobNumber > s_maxNumberOfParallelThreads) + requestedJobNumber = static_cast<unsigned>(s_maxNumberOfParallelThreads); + + if (!s_threadPool) + s_threadPool = new Vector< RefPtr<ThreadPrivate> >(); + + // The main thread should be also a worker. + int maxNumberOfNewThreads = requestedJobNumber - 1; + + for (int i = 0; i < s_maxNumberOfParallelThreads && m_threads.size() < static_cast<unsigned>(maxNumberOfNewThreads); ++i) { + if (s_threadPool->size() < static_cast<unsigned>(i) + 1U) + s_threadPool->append(ThreadPrivate::create()); + + if ((*s_threadPool)[i]->tryLockFor(this)) + m_threads.append((*s_threadPool)[i]); + } + + m_numberOfJobs = m_threads.size() + 1; +} + +void ParallelEnvironment::execute(void* parameters) +{ + unsigned char* currentParameter = static_cast<unsigned char*>(parameters); + size_t i; + for (i = 0; i < m_threads.size(); ++i) { + m_threads[i]->execute(m_threadFunction, currentParameter); + currentParameter += m_sizeOfParameter; + } + + // The work for the main thread. + (*m_threadFunction)(currentParameter); + + // Wait until all jobs are done. + for (i = 0; i < m_threads.size(); ++i) + m_threads[i]->waitForFinish(); +} + +void ParallelEnvironment::determineMaxNumberOfParallelThreads() +{ + const int defaultIfUnavailable = 2; +#if OS(DARWIN) || OS(OPENBSD) || OS(NETBSD) + unsigned result; + size_t length = sizeof(result); + int name[] = { + CTL_HW, + HW_NCPU + }; + int sysctlResult = sysctl(name, sizeof(name) / sizeof(int), &result, &length, 0, 0); + s_maxNumberOfParallelThreads = sysctlResult < 0 ? defaultIfUnavailable : result; +#elif OS(LINUX) || OS(AIX) || OS(SOLARIS) + long sysconfResult = sysconf(_SC_NPROCESSORS_ONLN); + s_maxNumberOfParallelThreads = sysconfResult < 0 ? defaultIfUnavailable : static_cast<int>(sysconfResult); +#elif OS(WINDOWS) + UNUSED_PARAM(defaultIfUnavailable); + + SYSTEM_INFO sysInfo; + GetSystemInfo(&sysInfo); + s_maxNumberOfParallelThreads = sysInfo.dwNumberOfProcessors; +#else + s_maxNumberOfParallelThreads = defaultIfUnavailable; +#endif +} + +bool ParallelEnvironment::ThreadPrivate::tryLockFor(ParallelEnvironment* parent) +{ + bool locked = m_mutex.tryLock(); + + if (!locked) + return false; + + if (m_parent) { + m_mutex.unlock(); + return false; + } + + if (!m_threadID) + m_threadID = createThread(&ParallelEnvironment::ThreadPrivate::workerThread, this, "Parallel worker"); + + if (m_threadID) + m_parent = parent; + + m_mutex.unlock(); + return m_threadID; +} + +void ParallelEnvironment::ThreadPrivate::execute(ThreadFunction threadFunction, void* parameters) +{ + MutexLocker lock(m_mutex); + + m_threadFunction = threadFunction; + m_parameters = parameters; + m_running = true; + m_threadCondition.signal(); +} + +void ParallelEnvironment::ThreadPrivate::waitForFinish() +{ + MutexLocker lock(m_mutex); + + while (m_running) + m_threadCondition.wait(m_mutex); +} + +void* ParallelEnvironment::ThreadPrivate::workerThread(void* threadData) +{ + ThreadPrivate* sharedThread = reinterpret_cast<ThreadPrivate*>(threadData); + MutexLocker lock(sharedThread->m_mutex); + + while (sharedThread->m_threadID) { + if (sharedThread->m_running) { + (*sharedThread->m_threadFunction)(sharedThread->m_parameters); + sharedThread->m_running = false; + sharedThread->m_parent = 0; + sharedThread->m_threadCondition.signal(); + } + + sharedThread->m_threadCondition.wait(sharedThread->m_mutex); + } + return 0; +} + +} // namespace WTF +#endif // ENABLE(THREADING_GENERIC) diff --git a/Source/JavaScriptCore/wtf/ParallelJobsGeneric.h b/Source/JavaScriptCore/wtf/ParallelJobsGeneric.h new file mode 100644 index 000000000..dab6dd9fb --- /dev/null +++ b/Source/JavaScriptCore/wtf/ParallelJobsGeneric.h @@ -0,0 +1,103 @@ +/* + * Copyright (C) 2011 University of Szeged + * Copyright (C) 2011 Gabor Loki <loki@webkit.org> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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 UNIVERSITY OF SZEGED ``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 UNIVERSITY OF SZEGED 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. + */ + +#ifndef ParallelJobsGeneric_h +#define ParallelJobsGeneric_h + +#if ENABLE(THREADING_GENERIC) + +#include <wtf/RefCounted.h> +#include <wtf/Threading.h> + +namespace WTF { + +class ParallelEnvironment { + WTF_MAKE_FAST_ALLOCATED; +public: + typedef void (*ThreadFunction)(void*); + + ParallelEnvironment(ThreadFunction, size_t sizeOfParameter, int requestedJobNumber); + + int numberOfJobs() + { + return m_numberOfJobs; + } + + void execute(void* parameters); + + class ThreadPrivate : public RefCounted<ThreadPrivate> { + public: + ThreadPrivate() + : m_threadID(0) + , m_running(false) + , m_parent(0) + { + } + + bool tryLockFor(ParallelEnvironment*); + + void execute(ThreadFunction, void*); + + void waitForFinish(); + + static PassRefPtr<ThreadPrivate> create() + { + return adoptRef(new ThreadPrivate()); + } + + static void* workerThread(void*); + + private: + ThreadIdentifier m_threadID; + bool m_running; + ParallelEnvironment* m_parent; + + mutable Mutex m_mutex; + ThreadCondition m_threadCondition; + + ThreadFunction m_threadFunction; + void* m_parameters; + }; + +private: + static void determineMaxNumberOfParallelThreads(); + + ThreadFunction m_threadFunction; + size_t m_sizeOfParameter; + int m_numberOfJobs; + + Vector< RefPtr<ThreadPrivate> > m_threads; + static Vector< RefPtr<ThreadPrivate> >* s_threadPool; + static int s_maxNumberOfParallelThreads; +}; + +} // namespace WTF + +#endif // ENABLE(THREADING_GENERIC) + + +#endif // ParallelJobsGeneric_h diff --git a/Source/JavaScriptCore/wtf/ParallelJobsLibdispatch.h b/Source/JavaScriptCore/wtf/ParallelJobsLibdispatch.h new file mode 100644 index 000000000..ca7d9a4e8 --- /dev/null +++ b/Source/JavaScriptCore/wtf/ParallelJobsLibdispatch.h @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2011 University of Szeged + * Copyright (C) 2011 Gabor Loki <loki@webkit.org> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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 UNIVERSITY OF SZEGED ``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 UNIVERSITY OF SZEGED 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. + */ + +#ifndef ParallelJobsLibdispatch_h +#define ParallelJobsLibdispatch_h + +#if ENABLE(THREADING_LIBDISPATCH) + +#include <dispatch/dispatch.h> + +namespace WTF { + +class ParallelEnvironment { + WTF_MAKE_FAST_ALLOCATED; +public: + typedef void (*ThreadFunction)(void*); + + ParallelEnvironment(ThreadFunction threadFunction, size_t sizeOfParameter, int requestedJobNumber) + : m_threadFunction(threadFunction) + , m_sizeOfParameter(sizeOfParameter) + , m_numberOfJobs(requestedJobNumber) + { + // We go with the requested number of jobs. libdispatch will distribute the work optimally. + ASSERT_ARG(requestedJobNumber, requestedJobNumber > 0); + } + + int numberOfJobs() + { + return m_numberOfJobs; + } + + void execute(unsigned char* parameters) + { + static dispatch_queue_t globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); + + dispatch_apply(m_numberOfJobs, globalQueue, ^(size_t i) { (*m_threadFunction)(parameters + (m_sizeOfParameter * i)); }); + } + +private: + ThreadFunction m_threadFunction; + size_t m_sizeOfParameter; + int m_numberOfJobs; +}; + +} // namespace WTF + +#endif // ENABLE(THREADING_LIBDISPATCH) + +#endif // ParallelJobsLibdispatch_h diff --git a/Source/JavaScriptCore/wtf/ParallelJobsOpenMP.h b/Source/JavaScriptCore/wtf/ParallelJobsOpenMP.h new file mode 100644 index 000000000..706bd8065 --- /dev/null +++ b/Source/JavaScriptCore/wtf/ParallelJobsOpenMP.h @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2011 University of Szeged + * Copyright (C) 2011 Gabor Loki <loki@webkit.org> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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 UNIVERSITY OF SZEGED ``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 UNIVERSITY OF SZEGED 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. + */ + +#ifndef ParallelJobsOpenMP_h +#define ParallelJobsOpenMP_h + +#if ENABLE(THREADING_OPENMP) + +#include <omp.h> + +namespace WTF { + +class ParallelEnvironment { + WTF_MAKE_NONCOPYABLE(ParallelEnvironment); +public: + typedef void (*ThreadFunction)(void*); + + ParallelEnvironment(ThreadFunction threadFunction, size_t sizeOfParameter, int requestedJobNumber) : + m_threadFunction(threadFunction), + m_sizeOfParameter(sizeOfParameter) + { + int maxNumberOfThreads = omp_get_max_threads(); + + if (!requestedJobNumber || requestedJobNumber > maxNumberOfThreads) + requestedJobNumber = maxNumberOfThreads; + + ASSERT(requestedJobNumber > 0); + + m_numberOfJobs = requestedJobNumber; + + } + + int numberOfJobs() + { + return m_numberOfJobs; + } + + void execute(unsigned char* parameters) + { + omp_set_num_threads(m_numberOfJobs); + +#pragma omp parallel for + for (int i = 0; i < m_numberOfJobs; ++i) + (*m_threadFunction)(parameters + i * m_sizeOfParameter); + } + +private: + ThreadFunction m_threadFunction; + size_t m_sizeOfParameter; + int m_numberOfJobs; +}; + +} // namespace WTF + +#endif // ENABLE(THREADING_OPENMP) + +#endif // ParallelJobsOpenMP_h diff --git a/Source/JavaScriptCore/wtf/PassOwnArrayPtr.h b/Source/JavaScriptCore/wtf/PassOwnArrayPtr.h new file mode 100644 index 000000000..3748f18c6 --- /dev/null +++ b/Source/JavaScriptCore/wtf/PassOwnArrayPtr.h @@ -0,0 +1,169 @@ +/* + * Copyright (C) 2010 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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 APPLE INC. ``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 APPLE INC. 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. + */ + +#ifndef WTF_PassOwnArrayPtr_h +#define WTF_PassOwnArrayPtr_h + +#include "Assertions.h" +#include "NullPtr.h" +#include "TypeTraits.h" + +namespace WTF { + +template<typename T> class OwnArrayPtr; +template<typename T> class PassOwnArrayPtr; +template<typename T> PassOwnArrayPtr<T> adoptArrayPtr(T*); +template<typename T> void deleteOwnedArrayPtr(T* ptr); + +template<typename T> class PassOwnArrayPtr { +public: + typedef T* PtrType; + + PassOwnArrayPtr() : m_ptr(0) { } + PassOwnArrayPtr(std::nullptr_t) : m_ptr(0) { } + + // It somewhat breaks the type system to allow transfer of ownership out of + // a const PassOwnArrayPtr. However, it makes it much easier to work with PassOwnArrayPtr + // temporaries, and we don't have a need to use real const PassOwnArrayPtrs anyway. + PassOwnArrayPtr(const PassOwnArrayPtr& o) : m_ptr(o.leakPtr()) { } + template<typename U> PassOwnArrayPtr(const PassOwnArrayPtr<U>& o) : m_ptr(o.leakPtr()) { } + + ~PassOwnArrayPtr() { deleteOwnedArrayPtr(m_ptr); } + + PtrType get() const { return m_ptr; } + + PtrType leakPtr() const WARN_UNUSED_RETURN; + + T& operator*() const { ASSERT(m_ptr); return *m_ptr; } + PtrType operator->() const { ASSERT(m_ptr); return m_ptr; } + + bool operator!() const { return !m_ptr; } + + // This conversion operator allows implicit conversion to bool but not to other integer types. + typedef PtrType PassOwnArrayPtr::*UnspecifiedBoolType; + operator UnspecifiedBoolType() const { return m_ptr ? &PassOwnArrayPtr::m_ptr : 0; } + + PassOwnArrayPtr& operator=(const PassOwnArrayPtr&) { COMPILE_ASSERT(!sizeof(T*), PassOwnArrayPtr_should_never_be_assigned_to); return *this; } + + template<typename U> friend PassOwnArrayPtr<U> adoptArrayPtr(U*); + +private: + explicit PassOwnArrayPtr(PtrType ptr) : m_ptr(ptr) { } + + mutable PtrType m_ptr; +}; + +template<typename T> inline typename PassOwnArrayPtr<T>::PtrType PassOwnArrayPtr<T>::leakPtr() const +{ + PtrType ptr = m_ptr; + m_ptr = 0; + return ptr; +} + +template<typename T, typename U> inline bool operator==(const PassOwnArrayPtr<T>& a, const PassOwnArrayPtr<U>& b) +{ + return a.get() == b.get(); +} + +template<typename T, typename U> inline bool operator==(const PassOwnArrayPtr<T>& a, const OwnArrayPtr<U>& b) +{ + return a.get() == b.get(); +} + +template<typename T, typename U> inline bool operator==(const OwnArrayPtr<T>& a, const PassOwnArrayPtr<U>& b) +{ + return a.get() == b.get(); +} + +template<typename T, typename U> inline bool operator==(const PassOwnArrayPtr<T>& a, U* b) +{ + return a.get() == b; +} + +template<typename T, typename U> inline bool operator==(T* a, const PassOwnArrayPtr<U>& b) +{ + return a == b.get(); +} + +template<typename T, typename U> inline bool operator!=(const PassOwnArrayPtr<T>& a, const PassOwnArrayPtr<U>& b) +{ + return a.get() != b.get(); +} + +template<typename T, typename U> inline bool operator!=(const PassOwnArrayPtr<T>& a, const OwnArrayPtr<U>& b) +{ + return a.get() != b.get(); +} + +template<typename T, typename U> inline bool operator!=(const OwnArrayPtr<T>& a, const PassOwnArrayPtr<U>& b) +{ + return a.get() != b.get(); +} + +template<typename T, typename U> inline bool operator!=(const PassOwnArrayPtr<T>& a, U* b) +{ + return a.get() != b; +} + +template<typename T, typename U> inline bool operator!=(T* a, const PassOwnArrayPtr<U>& b) +{ + return a != b.get(); +} + +template<typename T> inline PassOwnArrayPtr<T> adoptArrayPtr(T* ptr) +{ + return PassOwnArrayPtr<T>(ptr); +} + +template<typename T> inline void deleteOwnedArrayPtr(T* ptr) +{ + typedef char known[sizeof(T) ? 1 : -1]; + if (sizeof(known)) + delete [] ptr; +} + +template<typename T, typename U> inline PassOwnArrayPtr<T> static_pointer_cast(const PassOwnArrayPtr<U>& p) +{ + return adoptArrayPtr(static_cast<T*>(p.leakPtr())); +} + +template<typename T, typename U> inline PassOwnArrayPtr<T> const_pointer_cast(const PassOwnArrayPtr<U>& p) +{ + return adoptArrayPtr(const_cast<T*>(p.leakPtr())); +} + +template<typename T> inline T* getPtr(const PassOwnArrayPtr<T>& p) +{ + return p.get(); +} + +} // namespace WTF + +using WTF::PassOwnArrayPtr; +using WTF::adoptArrayPtr; +using WTF::const_pointer_cast; +using WTF::static_pointer_cast; + +#endif // WTF_PassOwnArrayPtr_h diff --git a/Source/JavaScriptCore/wtf/PassOwnPtr.h b/Source/JavaScriptCore/wtf/PassOwnPtr.h new file mode 100644 index 000000000..262ac3bfe --- /dev/null +++ b/Source/JavaScriptCore/wtf/PassOwnPtr.h @@ -0,0 +1,172 @@ +/* + * Copyright (C) 2009, 2010 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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 APPLE INC. ``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 APPLE INC. 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. + */ + +#ifndef WTF_PassOwnPtr_h +#define WTF_PassOwnPtr_h + +#include "Assertions.h" +#include "NullPtr.h" +#include "OwnPtrCommon.h" +#include "TypeTraits.h" + +namespace WTF { + + // Unlike most of our smart pointers, PassOwnPtr can take either the pointer type or the pointed-to type. + + template<typename T> class OwnPtr; + template<typename T> class PassOwnPtr; + template<typename T> PassOwnPtr<T> adoptPtr(T*); + + template<typename T> class PassOwnPtr { + public: + typedef typename RemovePointer<T>::Type ValueType; + typedef ValueType* PtrType; + + PassOwnPtr() : m_ptr(0) { } + PassOwnPtr(std::nullptr_t) : m_ptr(0) { } + + // It somewhat breaks the type system to allow transfer of ownership out of + // a const PassOwnPtr. However, it makes it much easier to work with PassOwnPtr + // temporaries, and we don't have a need to use real const PassOwnPtrs anyway. + PassOwnPtr(const PassOwnPtr& o) : m_ptr(o.leakPtr()) { } + template<typename U> PassOwnPtr(const PassOwnPtr<U>& o) : m_ptr(o.leakPtr()) { } + + ~PassOwnPtr() { deleteOwnedPtr(m_ptr); } + + PtrType get() const { return m_ptr; } + + PtrType leakPtr() const WARN_UNUSED_RETURN; + + ValueType& operator*() const { ASSERT(m_ptr); return *m_ptr; } + PtrType operator->() const { ASSERT(m_ptr); return m_ptr; } + + bool operator!() const { return !m_ptr; } + + // This conversion operator allows implicit conversion to bool but not to other integer types. + typedef PtrType PassOwnPtr::*UnspecifiedBoolType; + operator UnspecifiedBoolType() const { return m_ptr ? &PassOwnPtr::m_ptr : 0; } + + PassOwnPtr& operator=(const PassOwnPtr&) { COMPILE_ASSERT(!sizeof(T*), PassOwnPtr_should_never_be_assigned_to); return *this; } + + template<typename U> friend PassOwnPtr<U> adoptPtr(U*); + + private: + explicit PassOwnPtr(PtrType ptr) : m_ptr(ptr) { } + + // We should never have two OwnPtrs for the same underlying object (otherwise we'll get + // double-destruction), so these equality operators should never be needed. + template<typename U> bool operator==(const PassOwnPtr<U>&) { COMPILE_ASSERT(!sizeof(U*), OwnPtrs_should_never_be_equal); return false; } + template<typename U> bool operator!=(const PassOwnPtr<U>&) { COMPILE_ASSERT(!sizeof(U*), OwnPtrs_should_never_be_equal); return false; } + template<typename U> bool operator==(const OwnPtr<U>&) { COMPILE_ASSERT(!sizeof(U*), OwnPtrs_should_never_be_equal); return false; } + template<typename U> bool operator!=(const OwnPtr<U>&) { COMPILE_ASSERT(!sizeof(U*), OwnPtrs_should_never_be_equal); return false; } + + mutable PtrType m_ptr; + }; + + template<typename T> inline typename PassOwnPtr<T>::PtrType PassOwnPtr<T>::leakPtr() const + { + PtrType ptr = m_ptr; + m_ptr = 0; + return ptr; + } + + template<typename T, typename U> inline bool operator==(const PassOwnPtr<T>& a, const PassOwnPtr<U>& b) + { + return a.get() == b.get(); + } + + template<typename T, typename U> inline bool operator==(const PassOwnPtr<T>& a, const OwnPtr<U>& b) + { + return a.get() == b.get(); + } + + template<typename T, typename U> inline bool operator==(const OwnPtr<T>& a, const PassOwnPtr<U>& b) + { + return a.get() == b.get(); + } + + template<typename T, typename U> inline bool operator==(const PassOwnPtr<T>& a, U* b) + { + return a.get() == b; + } + + template<typename T, typename U> inline bool operator==(T* a, const PassOwnPtr<U>& b) + { + return a == b.get(); + } + + template<typename T, typename U> inline bool operator!=(const PassOwnPtr<T>& a, const PassOwnPtr<U>& b) + { + return a.get() != b.get(); + } + + template<typename T, typename U> inline bool operator!=(const PassOwnPtr<T>& a, const OwnPtr<U>& b) + { + return a.get() != b.get(); + } + + template<typename T, typename U> inline bool operator!=(const OwnPtr<T>& a, const PassOwnPtr<U>& b) + { + return a.get() != b.get(); + } + + template<typename T, typename U> inline bool operator!=(const PassOwnPtr<T>& a, U* b) + { + return a.get() != b; + } + + template<typename T, typename U> inline bool operator!=(T* a, const PassOwnPtr<U>& b) + { + return a != b.get(); + } + + template<typename T> inline PassOwnPtr<T> adoptPtr(T* ptr) + { + return PassOwnPtr<T>(ptr); + } + + template<typename T, typename U> inline PassOwnPtr<T> static_pointer_cast(const PassOwnPtr<U>& p) + { + return adoptPtr(static_cast<T*>(p.leakPtr())); + } + + template<typename T, typename U> inline PassOwnPtr<T> const_pointer_cast(const PassOwnPtr<U>& p) + { + return adoptPtr(const_cast<T*>(p.leakPtr())); + } + + template<typename T> inline T* getPtr(const PassOwnPtr<T>& p) + { + return p.get(); + } + +} // namespace WTF + +using WTF::PassOwnPtr; +using WTF::adoptPtr; +using WTF::const_pointer_cast; +using WTF::static_pointer_cast; + +#endif // WTF_PassOwnPtr_h diff --git a/Source/JavaScriptCore/wtf/PassRefPtr.h b/Source/JavaScriptCore/wtf/PassRefPtr.h new file mode 100644 index 000000000..a13dee794 --- /dev/null +++ b/Source/JavaScriptCore/wtf/PassRefPtr.h @@ -0,0 +1,245 @@ +/* + * Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010 Apple Inc. All rights reserved. + * + * This library 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 library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#ifndef WTF_PassRefPtr_h +#define WTF_PassRefPtr_h + +#include "AlwaysInline.h" +#include "NullPtr.h" + +namespace WTF { + + template<typename T> class RefPtr; + template<typename T> class PassRefPtr; + template<typename T> PassRefPtr<T> adoptRef(T*); + + inline void adopted(const void*) { } + +#if !PLATFORM(QT) + #define REF_DEREF_INLINE ALWAYS_INLINE +#else + // Using ALWAYS_INLINE broke the Qt build. This may be a GCC bug. + // See https://bugs.webkit.org/show_bug.cgi?id=37253 for details. + #define REF_DEREF_INLINE inline +#endif + + template<typename T> REF_DEREF_INLINE void refIfNotNull(T* ptr) + { + if (LIKELY(ptr != 0)) + ptr->ref(); + } + + template<typename T> REF_DEREF_INLINE void derefIfNotNull(T* ptr) + { + if (LIKELY(ptr != 0)) + ptr->deref(); + } + + #undef REF_DEREF_INLINE + + template<typename T> class PassRefPtr { + public: + PassRefPtr() : m_ptr(0) { } + PassRefPtr(T* ptr) : m_ptr(ptr) { refIfNotNull(ptr); } + // It somewhat breaks the type system to allow transfer of ownership out of + // a const PassRefPtr. However, it makes it much easier to work with PassRefPtr + // temporaries, and we don't have a need to use real const PassRefPtrs anyway. + PassRefPtr(const PassRefPtr& o) : m_ptr(o.leakRef()) { } + template<typename U> PassRefPtr(const PassRefPtr<U>& o) : m_ptr(o.leakRef()) { } + + ALWAYS_INLINE ~PassRefPtr() { derefIfNotNull(m_ptr); } + + template<typename U> PassRefPtr(const RefPtr<U>&); + + T* get() const { return m_ptr; } + + T* leakRef() const WARN_UNUSED_RETURN; + + T& operator*() const { return *m_ptr; } + T* operator->() const { return m_ptr; } + + bool operator!() const { return !m_ptr; } + + // This conversion operator allows implicit conversion to bool but not to other integer types. + typedef T* (PassRefPtr::*UnspecifiedBoolType); + operator UnspecifiedBoolType() const { return m_ptr ? &PassRefPtr::m_ptr : 0; } + + PassRefPtr& operator=(const PassRefPtr&) { COMPILE_ASSERT(!sizeof(T*), PassRefPtr_should_never_be_assigned_to); return *this; } + + friend PassRefPtr adoptRef<T>(T*); + + private: + // adopting constructor + PassRefPtr(T* ptr, bool) : m_ptr(ptr) { } + + mutable T* m_ptr; + }; + + // NonNullPassRefPtr: Optimized for passing non-null pointers. A NonNullPassRefPtr + // begins life non-null, and can only become null through a call to leakRef() + // or clear(). + + // FIXME: NonNullPassRefPtr could just inherit from PassRefPtr. However, + // if we use inheritance, GCC's optimizer fails to realize that destruction + // of a released NonNullPassRefPtr is a no-op. So, for now, just copy the + // most important code from PassRefPtr. + template<typename T> class NonNullPassRefPtr { + public: + NonNullPassRefPtr(T* ptr) + : m_ptr(ptr) + { + ASSERT(m_ptr); + m_ptr->ref(); + } + + template<typename U> NonNullPassRefPtr(const RefPtr<U>& o) + : m_ptr(o.get()) + { + ASSERT(m_ptr); + m_ptr->ref(); + } + + NonNullPassRefPtr(const NonNullPassRefPtr& o) + : m_ptr(o.leakRef()) + { + ASSERT(m_ptr); + } + + template<typename U> NonNullPassRefPtr(const NonNullPassRefPtr<U>& o) + : m_ptr(o.leakRef()) + { + ASSERT(m_ptr); + } + + template<typename U> NonNullPassRefPtr(const PassRefPtr<U>& o) + : m_ptr(o.leakRef()) + { + ASSERT(m_ptr); + } + + ALWAYS_INLINE ~NonNullPassRefPtr() { derefIfNotNull(m_ptr); } + + T* get() const { return m_ptr; } + + T* leakRef() const WARN_UNUSED_RETURN { T* tmp = m_ptr; m_ptr = 0; return tmp; } + + T& operator*() const { return *m_ptr; } + T* operator->() const { return m_ptr; } + + NonNullPassRefPtr& operator=(const NonNullPassRefPtr&) { COMPILE_ASSERT(!sizeof(T*), NonNullPassRefPtr_should_never_be_assigned_to); return *this; } + + private: + mutable T* m_ptr; + }; + + template<typename T> template<typename U> inline PassRefPtr<T>::PassRefPtr(const RefPtr<U>& o) + : m_ptr(o.get()) + { + T* ptr = m_ptr; + refIfNotNull(ptr); + } + + template<typename T> inline T* PassRefPtr<T>::leakRef() const + { + T* ptr = m_ptr; + m_ptr = 0; + return ptr; + } + + template<typename T, typename U> inline bool operator==(const PassRefPtr<T>& a, const PassRefPtr<U>& b) + { + return a.get() == b.get(); + } + + template<typename T, typename U> inline bool operator==(const PassRefPtr<T>& a, const RefPtr<U>& b) + { + return a.get() == b.get(); + } + + template<typename T, typename U> inline bool operator==(const RefPtr<T>& a, const PassRefPtr<U>& b) + { + return a.get() == b.get(); + } + + template<typename T, typename U> inline bool operator==(const PassRefPtr<T>& a, U* b) + { + return a.get() == b; + } + + template<typename T, typename U> inline bool operator==(T* a, const PassRefPtr<U>& b) + { + return a == b.get(); + } + + template<typename T, typename U> inline bool operator!=(const PassRefPtr<T>& a, const PassRefPtr<U>& b) + { + return a.get() != b.get(); + } + + template<typename T, typename U> inline bool operator!=(const PassRefPtr<T>& a, const RefPtr<U>& b) + { + return a.get() != b.get(); + } + + template<typename T, typename U> inline bool operator!=(const RefPtr<T>& a, const PassRefPtr<U>& b) + { + return a.get() != b.get(); + } + + template<typename T, typename U> inline bool operator!=(const PassRefPtr<T>& a, U* b) + { + return a.get() != b; + } + + template<typename T, typename U> inline bool operator!=(T* a, const PassRefPtr<U>& b) + { + return a != b.get(); + } + + template<typename T> inline PassRefPtr<T> adoptRef(T* p) + { + adopted(p); + return PassRefPtr<T>(p, true); + } + + template<typename T, typename U> inline PassRefPtr<T> static_pointer_cast(const PassRefPtr<U>& p) + { + return adoptRef(static_cast<T*>(p.leakRef())); + } + + template<typename T, typename U> inline PassRefPtr<T> const_pointer_cast(const PassRefPtr<U>& p) + { + return adoptRef(const_cast<T*>(p.leakRef())); + } + + template<typename T> inline T* getPtr(const PassRefPtr<T>& p) + { + return p.get(); + } + +} // namespace WTF + +using WTF::PassRefPtr; +using WTF::NonNullPassRefPtr; +using WTF::adoptRef; +using WTF::static_pointer_cast; +using WTF::const_pointer_cast; + +#endif // WTF_PassRefPtr_h diff --git a/Source/JavaScriptCore/wtf/PassTraits.h b/Source/JavaScriptCore/wtf/PassTraits.h new file mode 100644 index 000000000..346273486 --- /dev/null +++ b/Source/JavaScriptCore/wtf/PassTraits.h @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2011 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of Apple Computer, Inc. ("Apple") 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 APPLE AND ITS 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 APPLE OR ITS 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. + */ + +#ifndef WTF_PassTraits_h +#define WTF_PassTraits_h + +#include "OwnPtr.h" +#include "RefPtr.h" + +// The PassTraits template exists to help optimize (or make possible) use +// of WTF data structures with WTF smart pointers that have a Pass +// variant for transfer of ownership + +namespace WTF { + +template<typename T> struct PassTraits { + typedef T Type; + typedef T PassType; + static PassType transfer(Type& value) { return value; } +}; + +template<typename T> struct PassTraits<OwnPtr<T> > { + typedef OwnPtr<T> Type; + typedef PassOwnPtr<T> PassType; + static PassType transfer(Type& value) { return value.release(); } +}; + +template<typename T> struct PassTraits<RefPtr<T> > { + typedef RefPtr<T> Type; + typedef PassRefPtr<T> PassType; + static PassType transfer(Type& value) { return value.release(); } +}; + +} // namespace WTF + +using WTF::PassTraits; + +#endif // WTF_PassTraits_h diff --git a/Source/JavaScriptCore/wtf/Platform.h b/Source/JavaScriptCore/wtf/Platform.h new file mode 100644 index 000000000..bb1462e1e --- /dev/null +++ b/Source/JavaScriptCore/wtf/Platform.h @@ -0,0 +1,1163 @@ +/* + * Copyright (C) 2006, 2007, 2008, 2009 Apple Inc. All rights reserved. + * Copyright (C) 2007-2009 Torch Mobile, Inc. + * Copyright (C) 2010, 2011 Research In Motion Limited. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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 APPLE COMPUTER, INC. ``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 APPLE COMPUTER, INC. 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. + */ + +#ifndef WTF_Platform_h +#define WTF_Platform_h + +/* Include compiler specific macros */ +#include "Compiler.h" + +/* ==== PLATFORM handles OS, operating environment, graphics API, and + CPU. This macro will be phased out in favor of platform adaptation + macros, policy decision macros, and top-level port definitions. ==== */ +#define PLATFORM(WTF_FEATURE) (defined WTF_PLATFORM_##WTF_FEATURE && WTF_PLATFORM_##WTF_FEATURE) + + +/* ==== Platform adaptation macros: these describe properties of the target environment. ==== */ + +/* CPU() - the target CPU architecture */ +#define CPU(WTF_FEATURE) (defined WTF_CPU_##WTF_FEATURE && WTF_CPU_##WTF_FEATURE) +/* HAVE() - specific system features (headers, functions or similar) that are present or not */ +#define HAVE(WTF_FEATURE) (defined HAVE_##WTF_FEATURE && HAVE_##WTF_FEATURE) +/* OS() - underlying operating system; only to be used for mandated low-level services like + virtual memory, not to choose a GUI toolkit */ +#define OS(WTF_FEATURE) (defined WTF_OS_##WTF_FEATURE && WTF_OS_##WTF_FEATURE) + + +/* ==== Policy decision macros: these define policy choices for a particular port. ==== */ + +/* USE() - use a particular third-party library or optional OS service */ +#define USE(WTF_FEATURE) (defined WTF_USE_##WTF_FEATURE && WTF_USE_##WTF_FEATURE) +/* ENABLE() - turn on a specific feature of WebKit */ +#define ENABLE(WTF_FEATURE) (defined ENABLE_##WTF_FEATURE && ENABLE_##WTF_FEATURE) + + +/* ==== CPU() - the target CPU architecture ==== */ + +/* This also defines CPU(BIG_ENDIAN) or CPU(MIDDLE_ENDIAN) or neither, as appropriate. */ + +/* CPU(ALPHA) - DEC Alpha */ +#if defined(__alpha__) +#define WTF_CPU_ALPHA 1 +#endif + +/* CPU(IA64) - Itanium / IA-64 */ +#if defined(__ia64__) +#define WTF_CPU_IA64 1 +/* 32-bit mode on Itanium */ +#if !defined(__LP64__) +#define WTF_CPU_IA64_32 1 +#endif +#endif + +/* CPU(MIPS) - MIPS 32-bit */ +/* Note: Only O32 ABI is tested, so we enable it for O32 ABI for now. */ +#if (defined(mips) || defined(__mips__) || defined(MIPS) || defined(_MIPS_)) \ + && defined(_ABIO32) +#define WTF_CPU_MIPS 1 +#if defined(__MIPSEB__) +#define WTF_CPU_BIG_ENDIAN 1 +#endif +#define WTF_MIPS_PIC (defined __PIC__) +#define WTF_MIPS_ARCH __mips +#define WTF_MIPS_ISA(v) (defined WTF_MIPS_ARCH && WTF_MIPS_ARCH == v) +#define WTF_MIPS_ISA_AT_LEAST(v) (defined WTF_MIPS_ARCH && WTF_MIPS_ARCH >= v) +#define WTF_MIPS_ARCH_REV __mips_isa_rev +#define WTF_MIPS_ISA_REV(v) (defined WTF_MIPS_ARCH_REV && WTF_MIPS_ARCH_REV == v) +#define WTF_MIPS_DOUBLE_FLOAT (defined __mips_hard_float && !defined __mips_single_float) +#define WTF_MIPS_FP64 (defined __mips_fpr && __mips_fpr == 64) +/* MIPS requires allocators to use aligned memory */ +#define WTF_USE_ARENA_ALLOC_ALIGNMENT_INTEGER 1 +#endif /* MIPS */ + +/* CPU(PPC) - PowerPC 32-bit */ +#if defined(__ppc__) \ + || defined(__PPC__) \ + || defined(__powerpc__) \ + || defined(__powerpc) \ + || defined(__POWERPC__) \ + || defined(_M_PPC) \ + || defined(__PPC) +#define WTF_CPU_PPC 1 +#define WTF_CPU_BIG_ENDIAN 1 +#endif + +/* CPU(PPC64) - PowerPC 64-bit */ +#if defined(__ppc64__) \ + || defined(__PPC64__) +#define WTF_CPU_PPC64 1 +#define WTF_CPU_BIG_ENDIAN 1 +#endif + +/* CPU(SH4) - SuperH SH-4 */ +#if defined(__SH4__) +#define WTF_CPU_SH4 1 +#endif + +/* CPU(SPARC32) - SPARC 32-bit */ +#if defined(__sparc) && !defined(__arch64__) || defined(__sparcv8) +#define WTF_CPU_SPARC32 1 +#define WTF_CPU_BIG_ENDIAN 1 +#endif + +/* CPU(SPARC64) - SPARC 64-bit */ +#if defined(__sparc__) && defined(__arch64__) || defined (__sparcv9) +#define WTF_CPU_SPARC64 1 +#define WTF_CPU_BIG_ENDIAN 1 +#endif + +/* CPU(SPARC) - any SPARC, true for CPU(SPARC32) and CPU(SPARC64) */ +#if CPU(SPARC32) || CPU(SPARC64) +#define WTF_CPU_SPARC 1 +#endif + +/* CPU(S390X) - S390 64-bit */ +#if defined(__s390x__) +#define WTF_CPU_S390X 1 +#define WTF_CPU_BIG_ENDIAN 1 +#endif + +/* CPU(S390) - S390 32-bit */ +#if defined(__s390__) +#define WTF_CPU_S390 1 +#define WTF_CPU_BIG_ENDIAN 1 +#endif + +/* CPU(X86) - i386 / x86 32-bit */ +#if defined(__i386__) \ + || defined(i386) \ + || defined(_M_IX86) \ + || defined(_X86_) \ + || defined(__THW_INTEL) +#define WTF_CPU_X86 1 +#endif + +/* CPU(X86_64) - AMD64 / Intel64 / x86_64 64-bit */ +#if defined(__x86_64__) \ + || defined(_M_X64) +#define WTF_CPU_X86_64 1 +#endif + +/* CPU(ARM) - ARM, any version*/ +#if defined(arm) \ + || defined(__arm__) \ + || defined(ARM) \ + || defined(_ARM_) +#define WTF_CPU_ARM 1 + +#if defined(__ARMEB__) || (COMPILER(RVCT) && defined(__BIG_ENDIAN)) +#define WTF_CPU_BIG_ENDIAN 1 + +#elif !defined(__ARM_EABI__) \ + && !defined(__EABI__) \ + && !defined(__VFP_FP__) \ + && !defined(_WIN32_WCE) \ + && !defined(ANDROID) +#define WTF_CPU_MIDDLE_ENDIAN 1 + +#endif + +#define WTF_ARM_ARCH_AT_LEAST(N) (CPU(ARM) && WTF_ARM_ARCH_VERSION >= N) + +/* Set WTF_ARM_ARCH_VERSION */ +#if defined(__ARM_ARCH_4__) \ + || defined(__ARM_ARCH_4T__) \ + || defined(__MARM_ARMV4__) \ + || defined(_ARMV4I_) +#define WTF_ARM_ARCH_VERSION 4 + +#elif defined(__ARM_ARCH_5__) \ + || defined(__ARM_ARCH_5T__) \ + || defined(__MARM_ARMV5__) +#define WTF_ARM_ARCH_VERSION 5 + +#elif defined(__ARM_ARCH_5E__) \ + || defined(__ARM_ARCH_5TE__) \ + || defined(__ARM_ARCH_5TEJ__) +#define WTF_ARM_ARCH_VERSION 5 +/*ARMv5TE requires allocators to use aligned memory*/ +#define WTF_USE_ARENA_ALLOC_ALIGNMENT_INTEGER 1 + +#elif defined(__ARM_ARCH_6__) \ + || defined(__ARM_ARCH_6J__) \ + || defined(__ARM_ARCH_6K__) \ + || defined(__ARM_ARCH_6Z__) \ + || defined(__ARM_ARCH_6ZK__) \ + || defined(__ARM_ARCH_6T2__) \ + || defined(__ARMV6__) +#define WTF_ARM_ARCH_VERSION 6 + +#elif defined(__ARM_ARCH_7A__) \ + || defined(__ARM_ARCH_7R__) +#define WTF_ARM_ARCH_VERSION 7 + +/* RVCT sets _TARGET_ARCH_ARM */ +#elif defined(__TARGET_ARCH_ARM) +#define WTF_ARM_ARCH_VERSION __TARGET_ARCH_ARM + +#if defined(__TARGET_ARCH_5E) \ + || defined(__TARGET_ARCH_5TE) \ + || defined(__TARGET_ARCH_5TEJ) +/*ARMv5TE requires allocators to use aligned memory*/ +#define WTF_USE_ARENA_ALLOC_ALIGNMENT_INTEGER 1 +#endif + +#else +#define WTF_ARM_ARCH_VERSION 0 + +#endif + +/* Set WTF_THUMB_ARCH_VERSION */ +#if defined(__ARM_ARCH_4T__) +#define WTF_THUMB_ARCH_VERSION 1 + +#elif defined(__ARM_ARCH_5T__) \ + || defined(__ARM_ARCH_5TE__) \ + || defined(__ARM_ARCH_5TEJ__) +#define WTF_THUMB_ARCH_VERSION 2 + +#elif defined(__ARM_ARCH_6J__) \ + || defined(__ARM_ARCH_6K__) \ + || defined(__ARM_ARCH_6Z__) \ + || defined(__ARM_ARCH_6ZK__) \ + || defined(__ARM_ARCH_6M__) +#define WTF_THUMB_ARCH_VERSION 3 + +#elif defined(__ARM_ARCH_6T2__) \ + || defined(__ARM_ARCH_7__) \ + || defined(__ARM_ARCH_7A__) \ + || defined(__ARM_ARCH_7R__) \ + || defined(__ARM_ARCH_7M__) +#define WTF_THUMB_ARCH_VERSION 4 + +/* RVCT sets __TARGET_ARCH_THUMB */ +#elif defined(__TARGET_ARCH_THUMB) +#define WTF_THUMB_ARCH_VERSION __TARGET_ARCH_THUMB + +#else +#define WTF_THUMB_ARCH_VERSION 0 +#endif + + +/* CPU(ARMV5_OR_LOWER) - ARM instruction set v5 or earlier */ +/* On ARMv5 and below the natural alignment is required. + And there are some other differences for v5 or earlier. */ +#if !defined(ARMV5_OR_LOWER) && !WTF_ARM_ARCH_AT_LEAST(6) +#define WTF_CPU_ARMV5_OR_LOWER 1 +#endif + + +/* CPU(ARM_TRADITIONAL) - Thumb2 is not available, only traditional ARM (v4 or greater) */ +/* CPU(ARM_THUMB2) - Thumb2 instruction set is available */ +/* Only one of these will be defined. */ +#if !defined(WTF_CPU_ARM_TRADITIONAL) && !defined(WTF_CPU_ARM_THUMB2) +# if defined(thumb2) || defined(__thumb2__) \ + || ((defined(__thumb) || defined(__thumb__)) && WTF_THUMB_ARCH_VERSION == 4) +# define WTF_CPU_ARM_TRADITIONAL 0 +# define WTF_CPU_ARM_THUMB2 1 +# elif WTF_ARM_ARCH_AT_LEAST(4) +# define WTF_CPU_ARM_TRADITIONAL 1 +# define WTF_CPU_ARM_THUMB2 0 +# else +# error "Not supported ARM architecture" +# endif +#elif CPU(ARM_TRADITIONAL) && CPU(ARM_THUMB2) /* Sanity Check */ +# error "Cannot use both of WTF_CPU_ARM_TRADITIONAL and WTF_CPU_ARM_THUMB2 platforms" +#endif /* !defined(WTF_CPU_ARM_TRADITIONAL) && !defined(WTF_CPU_ARM_THUMB2) */ + +#if defined(__ARM_NEON__) && !defined(WTF_CPU_ARM_NEON) +#define WTF_CPU_ARM_NEON 1 +#endif + +#endif /* ARM */ + +#if CPU(ARM) || CPU(MIPS) || CPU(SH4) +#define WTF_CPU_NEEDS_ALIGNED_ACCESS 1 +#endif + +/* ==== OS() - underlying operating system; only to be used for mandated low-level services like + virtual memory, not to choose a GUI toolkit ==== */ + +/* OS(ANDROID) - Android */ +#ifdef ANDROID +#define WTF_OS_ANDROID 1 +#endif + +/* OS(AIX) - AIX */ +#ifdef _AIX +#define WTF_OS_AIX 1 +#endif + +/* OS(DARWIN) - Any Darwin-based OS, including Mac OS X and iPhone OS */ +#ifdef __APPLE__ +#define WTF_OS_DARWIN 1 + +#include <Availability.h> +#include <AvailabilityMacros.h> +#include <TargetConditionals.h> +#endif + +/* OS(IOS) - iOS */ +/* OS(MAC_OS_X) - Mac OS X (not including iOS) */ +#if OS(DARWIN) && ((defined(TARGET_OS_EMBEDDED) && TARGET_OS_EMBEDDED) \ + || (defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE) \ + || (defined(TARGET_IPHONE_SIMULATOR) && TARGET_IPHONE_SIMULATOR)) +#define WTF_OS_IOS 1 +#elif OS(DARWIN) && defined(TARGET_OS_MAC) && TARGET_OS_MAC +#define WTF_OS_MAC_OS_X 1 +/* FIXME: BUILDING_ON_.., and TARGETING... macros should be folded into the OS() system */ +#if !defined(MAC_OS_X_VERSION_10_6) || MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_6 +#define BUILDING_ON_LEOPARD 1 +#elif !defined(MAC_OS_X_VERSION_10_7) || MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_7 +#define BUILDING_ON_SNOW_LEOPARD 1 +#elif !defined(MAC_OS_X_VERSION_10_8) || MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_8 +#define BUILDING_ON_LION 1 +#endif +#if !defined(MAC_OS_X_VERSION_10_6) || MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_6 +#define TARGETING_LEOPARD 1 +#elif !defined(MAC_OS_X_VERSION_10_7) || MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_7 +#define TARGETING_SNOW_LEOPARD 1 +#elif !defined(MAC_OS_X_VERSION_10_8) || MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_8 +#define TARGETING_LION 1 +#endif +#endif + +/* OS(FREEBSD) - FreeBSD */ +#if defined(__FreeBSD__) || defined(__DragonFly__) || defined(__FreeBSD_kernel__) +#define WTF_OS_FREEBSD 1 +#endif + +/* OS(LINUX) - Linux */ +#ifdef __linux__ +#define WTF_OS_LINUX 1 +#endif + +/* OS(NETBSD) - NetBSD */ +#if defined(__NetBSD__) +#define WTF_OS_NETBSD 1 +#endif + +/* OS(OPENBSD) - OpenBSD */ +#ifdef __OpenBSD__ +#define WTF_OS_OPENBSD 1 +#endif + +/* OS(QNX) - QNX */ +#if defined(__QNXNTO__) +#define WTF_OS_QNX 1 +#endif + +/* OS(SOLARIS) - Solaris */ +#if defined(sun) || defined(__sun) +#define WTF_OS_SOLARIS 1 +#endif + +/* OS(WINCE) - Windows CE; note that for this platform OS(WINDOWS) is also defined */ +#if defined(_WIN32_WCE) +#define WTF_OS_WINCE 1 +#endif + +/* OS(WINDOWS) - Any version of Windows */ +#if defined(WIN32) || defined(_WIN32) +#define WTF_OS_WINDOWS 1 +#endif + +/* OS(UNIX) - Any Unix-like system */ +#if OS(AIX) \ + || OS(ANDROID) \ + || OS(DARWIN) \ + || OS(FREEBSD) \ + || OS(LINUX) \ + || OS(NETBSD) \ + || OS(OPENBSD) \ + || OS(QNX) \ + || OS(SOLARIS) \ + || defined(unix) \ + || defined(__unix) \ + || defined(__unix__) +#define WTF_OS_UNIX 1 +#endif + +/* Operating environments */ + +/* FIXME: these are all mixes of OS, operating environment and policy choices. */ +/* PLATFORM(CHROMIUM) */ +/* PLATFORM(QT) */ +/* PLATFORM(WX) */ +/* PLATFORM(GTK) */ +/* PLATFORM(BLACKBERRY) */ +/* PLATFORM(MAC) */ +/* PLATFORM(WIN) */ +#if defined(BUILDING_CHROMIUM__) +#define WTF_PLATFORM_CHROMIUM 1 +#elif defined(BUILDING_QT__) +#define WTF_PLATFORM_QT 1 +#elif defined(BUILDING_WX__) +#define WTF_PLATFORM_WX 1 +#elif defined(BUILDING_GTK__) +#define WTF_PLATFORM_GTK 1 +#elif defined(BUILDING_BLACKBERRY__) +#define WTF_PLATFORM_BLACKBERRY 1 +#elif OS(DARWIN) +#define WTF_PLATFORM_MAC 1 +#elif OS(WINDOWS) +#define WTF_PLATFORM_WIN 1 +#endif + +/* PLATFORM(IOS) */ +/* FIXME: this is sometimes used as an OS switch and sometimes for higher-level things */ +#if (defined(TARGET_OS_EMBEDDED) && TARGET_OS_EMBEDDED) || (defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE) +#define WTF_PLATFORM_IOS 1 +#endif + +/* PLATFORM(IOS_SIMULATOR) */ +#if defined(TARGET_IPHONE_SIMULATOR) && TARGET_IPHONE_SIMULATOR +#define WTF_PLATFORM_IOS 1 +#define WTF_PLATFORM_IOS_SIMULATOR 1 +#else +#define WTF_PLATFORM_IOS_SIMULATOR 0 +#endif + +#if !defined(WTF_PLATFORM_IOS) +#define WTF_PLATFORM_IOS 0 +#endif + +/* Graphics engines */ + +/* USE(CG) and PLATFORM(CI) */ +#if PLATFORM(MAC) || PLATFORM(IOS) +#define WTF_USE_CG 1 +#endif +#if PLATFORM(MAC) || PLATFORM(IOS) || (PLATFORM(WIN) && USE(CG)) +#define WTF_USE_CA 1 +#endif + +/* USE(SKIA) for Win/Linux, CG for Mac, unless enabled */ +#if PLATFORM(CHROMIUM) +#if OS(DARWIN) +#if USE(SKIA_ON_MAC_CHROMIUM) +#define WTF_USE_SKIA 1 +#else +#define WTF_USE_CG 1 +#endif +#define WTF_USE_ATSUI 1 +#define WTF_USE_CORE_TEXT 1 +#define WTF_USE_ICCJPEG 1 +#else +#define WTF_USE_SKIA 1 +#define WTF_USE_CHROMIUM_NET 1 +#endif +#endif + +#if PLATFORM(BLACKBERRY) +#define ENABLE_DRAG_SUPPORT 0 +#define USE_SYSTEM_MALLOC 1 +#define WTF_USE_MERSENNE_TWISTER_19937 1 +#define WTF_USE_SKIA 1 +#endif + +#if PLATFORM(GTK) +#define WTF_USE_CAIRO 1 +#endif + + +#if OS(WINCE) +#include <ce_time.h> +#define WTF_USE_MERSENNE_TWISTER_19937 1 +#endif + +/* On Windows, use QueryPerformanceCounter by default */ +#if OS(WINDOWS) +#define WTF_USE_QUERY_PERFORMANCE_COUNTER 1 +#endif + +#if OS(WINCE) && !PLATFORM(QT) +#define NOMINMAX /* Windows min and max conflict with standard macros */ +#define NOSHLWAPI /* shlwapi.h not available on WinCe */ + +/* MSDN documentation says these functions are provided with uspce.lib. But we cannot find this file. */ +#define __usp10__ /* disable "usp10.h" */ + +#define _INC_ASSERT /* disable "assert.h" */ +#define assert(x) + +#endif /* OS(WINCE) && !PLATFORM(QT) */ + +#if PLATFORM(QT) +#ifndef WTF_USE_ICU_UNICODE +#define WTF_USE_QT4_UNICODE 1 +#endif +#elif OS(WINCE) +#define WTF_USE_WINCE_UNICODE 1 +#elif PLATFORM(GTK) +/* The GTK+ Unicode backend is configurable */ +#else +#define WTF_USE_ICU_UNICODE 1 +#endif + +#if PLATFORM(MAC) && !PLATFORM(IOS) +#if !defined(BUILDING_ON_LEOPARD) && CPU(X86_64) +#define WTF_USE_PLUGIN_HOST_PROCESS 1 +#endif +#if !defined(BUILDING_ON_LEOPARD) && !defined(BUILDING_ON_SNOW_LEOPARD) +#define ENABLE_GESTURE_EVENTS 1 +#define ENABLE_RUBBER_BANDING 1 +#define WTF_USE_SCROLLBAR_PAINTER 1 +#endif +#if !defined(ENABLE_JAVA_BRIDGE) +#define ENABLE_JAVA_BRIDGE 1 +#endif +#if !defined(ENABLE_DASHBOARD_SUPPORT) +#define ENABLE_DASHBOARD_SUPPORT 1 +#endif +#define WTF_USE_CF 1 +#define WTF_USE_PTHREADS 1 +#define HAVE_PTHREAD_RWLOCK 1 +#define HAVE_READLINE 1 +#define HAVE_RUNLOOP_TIMER 1 +#define ENABLE_FULLSCREEN_API 1 +#define ENABLE_SMOOTH_SCROLLING 1 +#define ENABLE_WEB_ARCHIVE 1 +#define ENABLE_WEB_AUDIO 1 +#define ENABLE_VIDEO_TRACK 1 +#endif /* PLATFORM(MAC) && !PLATFORM(IOS) */ + +#if PLATFORM(CHROMIUM) && OS(DARWIN) +#define WTF_USE_CF 1 +#define WTF_USE_PTHREADS 1 +#define HAVE_PTHREAD_RWLOCK 1 + +#define WTF_USE_WK_SCROLLBAR_PAINTER 1 +#endif + +#if PLATFORM(IOS) +#define DONT_FINALIZE_ON_MAIN_THREAD 1 +#endif + +#if PLATFORM(QT) && OS(DARWIN) +#define WTF_USE_CF 1 +#define HAVE_DISPATCH_H 1 +#endif + +#if OS(DARWIN) && !PLATFORM(GTK) && !PLATFORM(QT) +#define ENABLE_PURGEABLE_MEMORY 1 +#endif + +#if PLATFORM(IOS) +#define ENABLE_CONTEXT_MENUS 0 +#define ENABLE_DRAG_SUPPORT 0 +#define ENABLE_DATA_TRANSFER_ITEMS 0 +#define ENABLE_FTPDIR 1 +#define ENABLE_GEOLOCATION 1 +#define ENABLE_ICONDATABASE 0 +#define ENABLE_INSPECTOR 1 +#define ENABLE_JAVA_BRIDGE 0 +#define ENABLE_NETSCAPE_PLUGIN_API 0 +#define ENABLE_ORIENTATION_EVENTS 1 +#define ENABLE_REPAINT_THROTTLING 1 +#define ENABLE_WEB_ARCHIVE 1 +#define HAVE_NETWORK_CFDATA_ARRAY_CALLBACK 1 +#define HAVE_PTHREAD_RWLOCK 1 +#define HAVE_READLINE 1 +#define HAVE_RUNLOOP_TIMER 0 +#define WTF_USE_CF 1 +#define WTF_USE_CFNETWORK 1 +#define WTF_USE_PTHREADS 1 + +#if PLATFORM(IOS_SIMULATOR) + #define ENABLE_INTERPRETER 1 + #define ENABLE_JIT 0 + #define ENABLE_YARR_JIT 0 +#else + #define ENABLE_INTERPRETER 1 + #define ENABLE_JIT 1 + #define ENABLE_YARR_JIT 1 +#endif + +#endif + +#if PLATFORM(WIN) && !OS(WINCE) +#define WTF_USE_CF 1 +#define WTF_USE_PTHREADS 0 +#endif + +#if PLATFORM(WIN) && !OS(WINCE) && !PLATFORM(CHROMIUM) && !PLATFORM(WIN_CAIRO) +#define WTF_USE_CFNETWORK 1 +#endif + +#if USE(CFNETWORK) || PLATFORM(MAC) || PLATFORM(IOS) +#define WTF_USE_CFURLCACHE 1 +#define WTF_USE_CFURLSTORAGESESSIONS 1 +#endif + +#if PLATFORM(WIN) && !OS(WINCE) && !PLATFORM(CHROMIUM) && !PLATFORM(QT) +#define ENABLE_WEB_ARCHIVE 1 +#endif + +#if PLATFORM(WIN) && !OS(WINCE) && !PLATFORM(CHROMIUM) && !PLATFORM(WIN_CAIRO) && !PLATFORM(QT) +#define ENABLE_FULLSCREEN_API 1 +#endif + +#if PLATFORM(WX) +#if !CPU(PPC) +#define ENABLE_ASSEMBLER 1 +#define ENABLE_JIT 1 +#endif +#define ENABLE_GLOBAL_FASTMALLOC_NEW 0 +#if OS(DARWIN) +#define WTF_USE_CF 1 +#define WTF_USE_CORE_TEXT 1 +#define ENABLE_WEB_ARCHIVE 1 +#endif +#endif + +#if PLATFORM(GTK) +#if HAVE(PTHREAD_H) +#define WTF_USE_PTHREADS 1 +#define HAVE_PTHREAD_RWLOCK 1 +#endif +#elif PLATFORM(QT) && OS(UNIX) +#define WTF_USE_PTHREADS 1 +#define HAVE_PTHREAD_RWLOCK 1 +#endif + +#if !defined(HAVE_ACCESSIBILITY) +#if PLATFORM(IOS) || PLATFORM(MAC) || PLATFORM(WIN) || PLATFORM(GTK) || PLATFORM(CHROMIUM) +#define HAVE_ACCESSIBILITY 1 +#endif +#endif /* !defined(HAVE_ACCESSIBILITY) */ + +#if OS(UNIX) +#define HAVE_SIGNAL_H 1 +#endif + +#if !defined(HAVE_VASPRINTF) +#if !COMPILER(MSVC) && !COMPILER(RVCT) && !COMPILER(MINGW) && !(COMPILER(GCC) && OS(QNX)) +#define HAVE_VASPRINTF 1 +#endif +#endif + +#if !defined(HAVE_STRNSTR) +#if OS(DARWIN) || (OS(FREEBSD) && !defined(__GLIBC__)) +#define HAVE_STRNSTR 1 +#endif +#endif + +#if !OS(WINDOWS) && !OS(SOLARIS) && !OS(QNX) \ + && !OS(RVCT) \ + && !OS(ANDROID) +#define HAVE_TM_GMTOFF 1 +#define HAVE_TM_ZONE 1 +#define HAVE_TIMEGM 1 +#endif + +#if OS(DARWIN) + +#define HAVE_ERRNO_H 1 +#define HAVE_LANGINFO_H 1 +#define HAVE_MMAP 1 +#define HAVE_MERGESORT 1 +#define HAVE_SBRK 1 +#define HAVE_STRINGS_H 1 +#define HAVE_SYS_PARAM_H 1 +#define HAVE_SYS_TIME_H 1 +#define HAVE_SYS_TIMEB_H 1 +#define WTF_USE_ACCELERATE 1 + +#ifndef TARGETING_LEOPARD + +#define HAVE_DISPATCH_H 1 +#define HAVE_HOSTED_CORE_ANIMATION 1 + +#if !PLATFORM(IOS) +#define HAVE_MADV_FREE_REUSE 1 +#define HAVE_MADV_FREE 1 +#define HAVE_PTHREAD_SETNAME_NP 1 +#endif + +#endif + +#if PLATFORM(IOS) +#define HAVE_MADV_FREE 1 +#define HAVE_PTHREAD_SETNAME_NP 1 +#endif + +#elif OS(WINDOWS) + +#if OS(WINCE) +#define HAVE_ERRNO_H 0 +#else +#define HAVE_SYS_TIMEB_H 1 +#define HAVE_ALIGNED_MALLOC 1 +#define HAVE_ISDEBUGGERPRESENT 1 +#endif +#define HAVE_VIRTUALALLOC 1 + +#elif OS(QNX) + +#define HAVE_ERRNO_H 1 +#define HAVE_MMAP 1 +#define HAVE_SBRK 1 +#define HAVE_STRINGS_H 1 +#define HAVE_SYS_PARAM_H 1 +#define HAVE_SYS_TIME_H 1 +#define WTF_USE_PTHREADS 1 + +#elif OS(ANDROID) + +#define HAVE_ERRNO_H 1 +#define HAVE_LANGINFO_H 0 +#define HAVE_NMAP 1 +#define HAVE_SBRK 1 +#define HAVE_STRINGS_H 1 +#define HAVE_SYS_PARAM_H 1 +#define HAVE_SYS_TIME_H 1 + +#else + +/* FIXME: is this actually used or do other platforms generate their own config.h? */ + +#define HAVE_ERRNO_H 1 +#define HAVE_LANGINFO_H 1 +#define HAVE_MMAP 1 +#define HAVE_SBRK 1 +#define HAVE_STRINGS_H 1 +#define HAVE_SYS_PARAM_H 1 +#define HAVE_SYS_TIME_H 1 + +#endif + +/* ENABLE macro defaults */ + +#if PLATFORM(QT) +/* We must not customize the global operator new and delete for the Qt port. */ +#define ENABLE_GLOBAL_FASTMALLOC_NEW 0 +#if !OS(UNIX) +#define USE_SYSTEM_MALLOC 1 +#endif +#endif + +/* fastMalloc match validation allows for runtime verification that + new is matched by delete, fastMalloc is matched by fastFree, etc. */ +#if !defined(ENABLE_FAST_MALLOC_MATCH_VALIDATION) +#define ENABLE_FAST_MALLOC_MATCH_VALIDATION 0 +#endif + +#if !defined(ENABLE_ICONDATABASE) +#define ENABLE_ICONDATABASE 1 +#endif + +#if !defined(ENABLE_SQL_DATABASE) +#define ENABLE_SQL_DATABASE 1 +#endif + +#if !defined(ENABLE_JAVASCRIPT_DEBUGGER) +#define ENABLE_JAVASCRIPT_DEBUGGER 1 +#endif + +#if !defined(ENABLE_FTPDIR) +#define ENABLE_FTPDIR 1 +#endif + +#if !defined(ENABLE_CONTEXT_MENUS) +#define ENABLE_CONTEXT_MENUS 1 +#endif + +#if !defined(ENABLE_DRAG_SUPPORT) +#define ENABLE_DRAG_SUPPORT 1 +#endif + +#if !defined(ENABLE_DATA_TRANSFER_ITEMS) +#define ENABLE_DATA_TRANSFER_ITEMS 0 +#endif + +#if !defined(ENABLE_DASHBOARD_SUPPORT) +#define ENABLE_DASHBOARD_SUPPORT 0 +#endif + +#if !defined(ENABLE_INSPECTOR) +#define ENABLE_INSPECTOR 1 +#endif + +#if !defined(ENABLE_JAVA_BRIDGE) +#define ENABLE_JAVA_BRIDGE 0 +#endif + +#if !defined(ENABLE_NETSCAPE_PLUGIN_API) +#define ENABLE_NETSCAPE_PLUGIN_API 1 +#endif + +#if !defined(ENABLE_NETSCAPE_PLUGIN_METADATA_CACHE) +#define ENABLE_NETSCAPE_PLUGIN_METADATA_CACHE 0 +#endif + +#if !defined(ENABLE_PURGEABLE_MEMORY) +#define ENABLE_PURGEABLE_MEMORY 0 +#endif + +#if !defined(WTF_USE_PLUGIN_HOST_PROCESS) +#define WTF_USE_PLUGIN_HOST_PROCESS 0 +#endif + +#if !defined(ENABLE_ORIENTATION_EVENTS) +#define ENABLE_ORIENTATION_EVENTS 0 +#endif + +#if !defined(ENABLE_OPCODE_STATS) +#define ENABLE_OPCODE_STATS 0 +#endif + +#if !defined(ENABLE_GLOBAL_FASTMALLOC_NEW) +#define ENABLE_GLOBAL_FASTMALLOC_NEW 1 +#endif + +#define ENABLE_DEBUG_WITH_BREAKPOINT 0 +#define ENABLE_SAMPLING_COUNTERS 0 +#define ENABLE_SAMPLING_FLAGS 0 +#define ENABLE_SAMPLING_REGIONS 0 +#define ENABLE_OPCODE_SAMPLING 0 +#define ENABLE_CODEBLOCK_SAMPLING 0 +#if ENABLE(CODEBLOCK_SAMPLING) && !ENABLE(OPCODE_SAMPLING) +#error "CODEBLOCK_SAMPLING requires OPCODE_SAMPLING" +#endif +#if ENABLE(OPCODE_SAMPLING) || ENABLE(SAMPLING_FLAGS) || ENABLE(SAMPLING_REGIONS) +#define ENABLE_SAMPLING_THREAD 1 +#endif + +#if !defined(ENABLE_GEOLOCATION) +#define ENABLE_GEOLOCATION 0 +#endif + +#if !defined(ENABLE_GESTURE_RECOGNIZER) +#define ENABLE_GESTURE_RECOGNIZER 0 +#endif + +#if !defined(ENABLE_VIEWPORT) +#define ENABLE_VIEWPORT 0 +#endif + +#if !defined(ENABLE_NOTIFICATIONS) +#define ENABLE_NOTIFICATIONS 0 +#endif + +#if PLATFORM(IOS) +#define ENABLE_TEXT_CARET 0 +#endif + +#if !defined(ENABLE_TEXT_CARET) +#define ENABLE_TEXT_CARET 1 +#endif + +#if !defined(ENABLE_FULLSCREEN_API) +#define ENABLE_FULLSCREEN_API 0 +#endif + +#if !defined(ENABLE_POINTER_LOCK) +#define ENABLE_POINTER_LOCK 0 +#endif + +#if !defined(WTF_USE_JSVALUE64) && !defined(WTF_USE_JSVALUE32_64) +#if (CPU(X86_64) && (OS(UNIX) || OS(WINDOWS))) \ + || (CPU(IA64) && !CPU(IA64_32)) \ + || CPU(ALPHA) \ + || CPU(SPARC64) \ + || CPU(S390X) \ + || CPU(PPC64) +#define WTF_USE_JSVALUE64 1 +#else +#define WTF_USE_JSVALUE32_64 1 +#endif +#endif /* !defined(WTF_USE_JSVALUE64) && !defined(WTF_USE_JSVALUE32_64) */ + +#if !defined(ENABLE_REPAINT_THROTTLING) +#define ENABLE_REPAINT_THROTTLING 0 +#endif + +/* Disable the JIT on versions of GCC prior to 4.1 */ +#if !defined(ENABLE_JIT) && COMPILER(GCC) && !GCC_VERSION_AT_LEAST(4, 1, 0) +#define ENABLE_JIT 0 +#endif + +/* JIT is not implemented for Windows 64-bit */ +#if !defined(ENABLE_JIT) && OS(WINDOWS) && CPU(X86_64) +#define ENABLE_JIT 0 +#endif + +/* The JIT is enabled by default on all x86, x64-64, ARM & MIPS platforms. */ +#if !defined(ENABLE_JIT) \ + && (CPU(X86) || CPU(X86_64) || CPU(ARM) || CPU(MIPS)) \ + && (OS(DARWIN) || !COMPILER(GCC) || GCC_VERSION_AT_LEAST(4, 1, 0)) \ + && !OS(WINCE) \ + && !OS(QNX) +#define ENABLE_JIT 1 +#endif + +#if !defined(ENABLE_DFG_JIT) && ENABLE(JIT) +/* Enable the DFG JIT on X86 and X86_64. Only tested on Mac and GNU/Linux. */ +#if (CPU(X86) || CPU(X86_64)) && (PLATFORM(MAC) || OS(LINUX)) +#define ENABLE_DFG_JIT 1 +#endif +/* Enable the DFG JIT on ARMv7. Only tested on iOS. */ +#if CPU(ARM_THUMB2) && PLATFORM(IOS) +#define ENABLE_DFG_JIT 1 +#endif +#endif + +/* Profiling of types and values used by JIT code. DFG_JIT depends on it, but you + can enable it manually with DFG turned off if you want to use it as a standalone + profiler. In that case, you probably want to also enable VERBOSE_VALUE_PROFILE + below. */ +#if !defined(ENABLE_VALUE_PROFILER) && ENABLE(DFG_JIT) +#define ENABLE_VALUE_PROFILER 1 +#endif + +#if !defined(ENABLE_VERBOSE_VALUE_PROFILE) && ENABLE(VALUE_PROFILER) +#define ENABLE_VERBOSE_VALUE_PROFILE 0 +#endif + +#if !defined(ENABLE_SIMPLE_HEAP_PROFILING) +#define ENABLE_SIMPLE_HEAP_PROFILING 0 +#endif + +/* Counts uses of write barriers using sampling counters. Be sure to also + set ENABLE_SAMPLING_COUNTERS to 1. */ +#if !defined(ENABLE_WRITE_BARRIER_PROFILING) +#define ENABLE_WRITE_BARRIER_PROFILING 0 +#endif + +/* Ensure that either the JIT or the interpreter has been enabled. */ +#if !defined(ENABLE_INTERPRETER) && !ENABLE(JIT) +#define ENABLE_INTERPRETER 1 +#endif +#if !(ENABLE(JIT) || ENABLE(INTERPRETER)) +#error You have to have at least one execution model enabled to build JSC +#endif + +#if CPU(SH4) && PLATFORM(QT) +#define ENABLE_JIT 1 +#endif + +/* Configure the JIT */ +#if CPU(ARM) +#if !defined(ENABLE_JIT_USE_SOFT_MODULO) && WTF_ARM_ARCH_AT_LEAST(5) +#define ENABLE_JIT_USE_SOFT_MODULO 1 +#endif +#endif + +#if CPU(X86) || CPU(X86_64) || CPU(MIPS) +#if !defined(ENABLE_JIT_USE_SOFT_MODULO) +#define ENABLE_JIT_USE_SOFT_MODULO 1 +#endif +#endif + +#if CPU(X86) && COMPILER(MSVC) +#define JSC_HOST_CALL __fastcall +#elif CPU(X86) && COMPILER(GCC) +#define JSC_HOST_CALL __attribute__ ((fastcall)) +#else +#define JSC_HOST_CALL +#endif + +/* Configure the interpreter */ +#if COMPILER(GCC) || (RVCT_VERSION_AT_LEAST(4, 0, 0, 0) && defined(__GNUC__)) +#define HAVE_COMPUTED_GOTO 1 +#endif +#if HAVE(COMPUTED_GOTO) && ENABLE(INTERPRETER) +#define ENABLE_COMPUTED_GOTO_INTERPRETER 1 +#endif + +/* Regular Expression Tracing - Set to 1 to trace RegExp's in jsc. Results dumped at exit */ +#define ENABLE_REGEXP_TRACING 0 + +/* Yet Another Regex Runtime - turned on by default for JIT enabled ports. */ +#if PLATFORM(CHROMIUM) +#define ENABLE_YARR_JIT 0 + +#elif ENABLE(JIT) && !defined(ENABLE_YARR_JIT) +#define ENABLE_YARR_JIT 1 + +/* Setting this flag compares JIT results with interpreter results. */ +#define ENABLE_YARR_JIT_DEBUG 0 +#endif + +#if ENABLE(JIT) || ENABLE(YARR_JIT) +#define ENABLE_ASSEMBLER 1 +#endif + +/* Pick which allocator to use; we only need an executable allocator if the assembler is compiled in. + On x86-64 we use a single fixed mmap, on other platforms we mmap on demand. */ +#if ENABLE(ASSEMBLER) +#if CPU(X86_64) || PLATFORM(IOS) +#define ENABLE_EXECUTABLE_ALLOCATOR_FIXED 1 +#else +#define ENABLE_EXECUTABLE_ALLOCATOR_DEMAND 1 +#endif +#endif + +#if !defined(ENABLE_PAN_SCROLLING) && OS(WINDOWS) +#define ENABLE_PAN_SCROLLING 1 +#endif + +#if !defined(ENABLE_SMOOTH_SCROLLING) +#define ENABLE_SMOOTH_SCROLLING 0 +#endif + +#if !defined(ENABLE_WEB_ARCHIVE) +#define ENABLE_WEB_ARCHIVE 0 +#endif + +/* Use the QXmlStreamReader implementation for XMLDocumentParser */ +/* Use the QXmlQuery implementation for XSLTProcessor */ +#if PLATFORM(QT) +#if !USE(LIBXML2) +#define WTF_USE_QXMLSTREAM 1 +#define WTF_USE_QXMLQUERY 1 +#endif +#endif + +#if PLATFORM(MAC) +/* Complex text framework */ +#ifndef BUILDING_ON_LEOPARD +#define WTF_USE_ATSUI 0 +#define WTF_USE_CORE_TEXT 1 +#else +#define WTF_USE_ATSUI 1 +#define WTF_USE_CORE_TEXT 0 +#endif +#endif + +/* Accelerated compositing */ +#if PLATFORM(MAC) || PLATFORM(IOS) || PLATFORM(QT) || (PLATFORM(WIN) && !OS(WINCE) && !PLATFORM(WIN_CAIRO)) || PLATFORM(EFL) +#define WTF_USE_ACCELERATED_COMPOSITING 1 +#endif + +#if (PLATFORM(MAC) && !defined(BUILDING_ON_LEOPARD)) || PLATFORM(IOS) +#define WTF_USE_PROTECTION_SPACE_AUTH_CALLBACK 1 +#endif + +#if !ENABLE(NETSCAPE_PLUGIN_API) || (ENABLE(NETSCAPE_PLUGIN_API) && ((OS(UNIX) && (PLATFORM(QT) || PLATFORM(WX))) || PLATFORM(GTK) || PLATFORM(EFL))) +#define ENABLE_PLUGIN_PACKAGE_SIMPLE_HASH 1 +#endif + +#if PLATFORM(MAC) && !defined(BUILDING_ON_LEOPARD) && !defined(BUILDING_ON_SNOW_LEOPARD) && !defined(BUILDING_ON_LION) +#define ENABLE_THREADED_SCROLLING 1 +#endif + +/* Set up a define for a common error that is intended to cause a build error -- thus the space after Error. */ +#define WTF_PLATFORM_CFNETWORK Error USE_macro_should_be_used_with_CFNETWORK + +/* FIXME: Eventually we should enable this for all platforms and get rid of the define. */ +#if PLATFORM(IOS) || PLATFORM(MAC) || PLATFORM(WIN) || PLATFORM(QT) +#define WTF_USE_PLATFORM_STRATEGIES 1 +#endif + +#if PLATFORM(WIN) +#define WTF_USE_CROSS_PLATFORM_CONTEXT_MENUS 1 +#endif + +/* Geolocation request policy. pre-emptive policy is to acquire user permission before acquiring location. + Client based implementations will have option to choose between pre-emptive and nonpre-emptive permission policy. + pre-emptive permission policy is enabled by default for all client-based implementations. */ +#if ENABLE(CLIENT_BASED_GEOLOCATION) +#define WTF_USE_PREEMPT_GEOLOCATION_PERMISSION 1 +#endif + +#if CPU(ARM_THUMB2) +#define ENABLE_BRANCH_COMPACTION 1 +#endif + +#if !defined(ENABLE_THREADING_LIBDISPATCH) && HAVE(DISPATCH_H) +#define ENABLE_THREADING_LIBDISPATCH 1 +#elif !defined(ENABLE_THREADING_OPENMP) && defined(_OPENMP) +#define ENABLE_THREADING_OPENMP 1 +#elif !defined(THREADING_GENERIC) +#define ENABLE_THREADING_GENERIC 1 +#endif + +#if ENABLE(GLIB_SUPPORT) +#include "GTypedefs.h" +#endif + +/* FIXME: This define won't be needed once #27551 is fully landed. However, + since most ports try to support sub-project independence, adding new headers + to WTF causes many ports to break, and so this way we can address the build + breakages one port at a time. */ +#define WTF_USE_EXPORT_MACROS 0 + +#if (PLATFORM(QT) && !OS(DARWIN)) || PLATFORM(GTK) || PLATFORM(EFL) +#define WTF_USE_UNIX_DOMAIN_SOCKETS 1 +#endif + +#if !defined(ENABLE_COMPARE_AND_SWAP) && COMPILER(GCC) && (CPU(X86) || CPU(X86_64) || CPU(ARM_THUMB2)) +#define ENABLE_COMPARE_AND_SWAP 1 +#endif + +#if !defined(ENABLE_PARALLEL_GC) && (PLATFORM(MAC) || PLATFORM(IOS)) && ENABLE(COMPARE_AND_SWAP) +#define ENABLE_PARALLEL_GC 1 +#endif + +#ifndef NDEBUG +#ifndef ENABLE_GC_VALIDATION +#define ENABLE_GC_VALIDATION 1 +#endif +#endif + +#if PLATFORM(MAC) && !defined(BUILDING_ON_LEOPARD) && !defined(BUILDING_ON_SNOW_LEOPARD) +#define WTF_USE_AVFOUNDATION 1 +#endif + +#if PLATFORM(MAC) || PLATFORM(GTK) || PLATFORM(EFL) || (PLATFORM(WIN) && !OS(WINCE) && !PLATFORM(WIN_CAIRO)) || PLATFORM(QT) +#define WTF_USE_REQUEST_ANIMATION_FRAME_TIMER 1 +#endif + +#if PLATFORM(MAC) +#define WTF_USE_REQUEST_ANIMATION_FRAME_DISPLAY_MONITOR 1 +#endif + +#if PLATFORM(MAC) && !defined(BUILDING_ON_LEOPARD) && !defined(BUILDING_ON_SNOW_LEOPARD) +#define HAVE_INVERTED_WHEEL_EVENTS 1 +#endif + +#if PLATFORM(MAC) +#define WTF_USE_COREAUDIO 1 +#endif + +#if PLATFORM(CHROMIUM) +#if !defined(WTF_USE_V8) +#define WTF_USE_V8 1 +#endif +#endif /* PLATFORM(CHROMIUM) */ + +#if !defined(WTF_USE_V8) +#define WTF_USE_V8 0 +#endif /* !defined(WTF_USE_V8) */ + +/* Using V8 implies not using JSC and vice versa */ +#define WTF_USE_JSC !WTF_USE_V8 + + +#endif /* WTF_Platform_h */ diff --git a/Source/JavaScriptCore/wtf/PlatformBlackBerry.cmake b/Source/JavaScriptCore/wtf/PlatformBlackBerry.cmake new file mode 100644 index 000000000..5f30d0889 --- /dev/null +++ b/Source/JavaScriptCore/wtf/PlatformBlackBerry.cmake @@ -0,0 +1,12 @@ +LIST(APPEND WTF_SOURCES + OSAllocatorPosix.cpp + TCSystemAlloc.cpp + ThreadIdentifierDataPthreads.cpp + ThreadingPthreads.cpp + blackberry/MainThreadBlackBerry.cpp + unicode/icu/CollatorICU.cpp +) + +LIST(INSERT WTF_INCLUDE_DIRECTORIES 0 + "${BLACKBERRY_THIRD_PARTY_DIR}/icu" +) diff --git a/Source/JavaScriptCore/wtf/PlatformEfl.cmake b/Source/JavaScriptCore/wtf/PlatformEfl.cmake new file mode 100644 index 000000000..5805c4180 --- /dev/null +++ b/Source/JavaScriptCore/wtf/PlatformEfl.cmake @@ -0,0 +1,50 @@ +LIST(APPEND WTF_SOURCES + efl/MainThreadEfl.cpp + efl/OwnPtrEfl.cpp + + OSAllocatorPosix.cpp + ThreadIdentifierDataPthreads.cpp + ThreadingPthreads.cpp + + unicode/icu/CollatorICU.cpp +) + +IF (ENABLE_GLIB_SUPPORT) + LIST(APPEND WTF_SOURCES + gobject/GOwnPtr.cpp + gobject/GRefPtr.cpp + ) + + LIST(APPEND WTF_INCLUDE_DIRECTORIES + ${Glib_INCLUDE_DIRS} + ${JAVASCRIPTCORE_DIR}/wtf/gobject + ) + + LIST(APPEND WTF_LIBRARIES + ${Glib_LIBRARIES} + ) +ENDIF () + +LIST(APPEND WTF_LIBRARIES + pthread + ${ICU_LIBRARIES} + ${ICU_I18N_LIBRARIES} + ${ECORE_LIBRARIES} + ${ECORE_EVAS_LIBRARIES} + ${EVAS_LIBRARIES} + ${CMAKE_DL_LIBS} +) + +LIST(APPEND WTF_LINK_FLAGS + ${ECORE_LDFLAGS} + ${ECORE_EVAS_LDFLAGS} + ${EVAS_LDFLAGS} +) + +LIST(APPEND WTF_INCLUDE_DIRECTORIES + ${ECORE_INCLUDE_DIRS} + ${ECORE_EVAS_INCLUDE_DIRS} + ${EVAS_INCLUDE_DIRS} + ${ICU_INCLUDE_DIRS} + ${JAVASCRIPTCORE_DIR}/wtf/unicode/ +) diff --git a/Source/JavaScriptCore/wtf/PlatformWinCE.cmake b/Source/JavaScriptCore/wtf/PlatformWinCE.cmake new file mode 100644 index 000000000..c34c5e5a5 --- /dev/null +++ b/Source/JavaScriptCore/wtf/PlatformWinCE.cmake @@ -0,0 +1,26 @@ +LIST(APPEND WTF_HEADERS + unicode/wince/UnicodeWinCE.h + + ${3RDPARTY_DIR}/ce-compat/ce_time.h + ${3RDPARTY_DIR}/ce-compat/ce_unicode.h +) + +LIST(APPEND WTF_SOURCES + NullPtr.cpp + OSAllocatorWin.cpp + ThreadingWin.cpp + ThreadSpecificWin.cpp + + unicode/CollatorDefault.cpp + unicode/wince/UnicodeWinCE.cpp + + win/MainThreadWin.cpp + win/OwnPtrWin.cpp + + ${3RDPARTY_DIR}/ce-compat/ce_time.c + ${3RDPARTY_DIR}/ce-compat/ce_unicode.cpp +) + +LIST(APPEND WTF_LIBRARIES + mmtimer +) diff --git a/Source/JavaScriptCore/wtf/PossiblyNull.h b/Source/JavaScriptCore/wtf/PossiblyNull.h new file mode 100644 index 000000000..79c4d8200 --- /dev/null +++ b/Source/JavaScriptCore/wtf/PossiblyNull.h @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2009 Apple Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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 APPLE INC. ``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 APPLE INC. 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. + */ + +#ifndef PossiblyNull_h +#define PossiblyNull_h + +#include "Assertions.h" + +namespace WTF { + +template <typename T> struct PossiblyNull { + PossiblyNull(T data) + : m_data(data) + { + } + PossiblyNull(const PossiblyNull<T>& source) + : m_data(source.m_data) + { + source.m_data = 0; + } + ~PossiblyNull() { ASSERT(!m_data); } + bool getValue(T& out) WARN_UNUSED_RETURN; +private: + mutable T m_data; +}; + +template <typename T> bool PossiblyNull<T>::getValue(T& out) +{ + out = m_data; + bool result = !!m_data; + m_data = 0; + return result; +} + +} + +#endif diff --git a/Source/JavaScriptCore/wtf/RandomNumber.cpp b/Source/JavaScriptCore/wtf/RandomNumber.cpp new file mode 100644 index 000000000..06074ed9f --- /dev/null +++ b/Source/JavaScriptCore/wtf/RandomNumber.cpp @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2006, 2007, 2008 Apple Inc. All rights reserved. + * (C) 2008, 2009 Torch Mobile Inc. All rights reserved. (http://www.torchmobile.com/) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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 APPLE COMPUTER, INC. ``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 APPLE COMPUTER, INC. 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 "config.h" +#include "RandomNumber.h" + +#include "CryptographicallyRandomNumber.h" +#include "RandomNumberSeed.h" + +#include <limits> +#include <limits.h> +#include <stdint.h> +#include <stdlib.h> + +#if USE(MERSENNE_TWISTER_19937) +extern "C" { +#include "mt19937ar.c" +} +#endif + +namespace WTF { + +double randomNumber() +{ +#if USE(OS_RANDOMNESS) + uint32_t bits = cryptographicallyRandomNumber(); + return static_cast<double>(bits) / (static_cast<double>(std::numeric_limits<uint32_t>::max()) + 1.0); +#else + // Without OS_RANDOMNESS, we fall back to other random number generators + // that might not be cryptographically secure. Ideally, most ports would + // define USE(OS_RANDOMNESS). + +#if USE(MERSENNE_TWISTER_19937) + return genrand_res53(); +#else + uint32_t part1 = rand() & (RAND_MAX - 1); + uint32_t part2 = rand() & (RAND_MAX - 1); + // rand only provides 31 bits, and the low order bits of that aren't very random + // so we take the high 26 bits of part 1, and the high 27 bits of part2. + part1 >>= 5; // drop the low 5 bits + part2 >>= 4; // drop the low 4 bits + uint64_t fullRandom = part1; + fullRandom <<= 27; + fullRandom |= part2; + + // Mask off the low 53bits + fullRandom &= (1LL << 53) - 1; + return static_cast<double>(fullRandom)/static_cast<double>(1LL << 53); +#endif +#endif +} + +} diff --git a/Source/JavaScriptCore/wtf/RandomNumber.h b/Source/JavaScriptCore/wtf/RandomNumber.h new file mode 100644 index 000000000..f2e7e8f50 --- /dev/null +++ b/Source/JavaScriptCore/wtf/RandomNumber.h @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2008 Torch Mobile Inc. All rights reserved. (http://www.torchmobile.com/) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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 APPLE COMPUTER, INC. ``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 APPLE COMPUTER, INC. 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. + */ + +#ifndef WTF_RandomNumber_h +#define WTF_RandomNumber_h + +namespace WTF { + + // Returns a pseudo-random number in the range [0, 1), attempts to be + // cryptographically secure if possible on the target platform + double randomNumber(); + +} + +using WTF::randomNumber; + +#endif diff --git a/Source/JavaScriptCore/wtf/RandomNumberSeed.h b/Source/JavaScriptCore/wtf/RandomNumberSeed.h new file mode 100644 index 000000000..b5547becb --- /dev/null +++ b/Source/JavaScriptCore/wtf/RandomNumberSeed.h @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2008 Torch Mobile Inc. All rights reserved. (http://www.torchmobile.com/) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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 APPLE COMPUTER, INC. ``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 APPLE COMPUTER, INC. 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. + */ + +#ifndef WTF_RandomNumberSeed_h +#define WTF_RandomNumberSeed_h + +#include <stdlib.h> +#include <time.h> + +#if HAVE(SYS_TIME_H) +#include <sys/time.h> +#endif + +#if OS(UNIX) +#include <sys/types.h> +#include <unistd.h> +#endif + +#if USE(MERSENNE_TWISTER_19937) +extern "C" { +void init_by_array(unsigned long init_key[],int key_length); +} +#endif + +// Internal JavaScriptCore usage only +namespace WTF { + +inline void initializeRandomNumberGenerator() +{ +#if OS(DARWIN) + // On Darwin we use arc4random which initialises itself. +#elif OS(WINCE) + // initialize rand() + srand(GetTickCount()); +#elif COMPILER(MSVC) && defined(_CRT_RAND_S) + // On Windows we use rand_s which initialises itself +#elif OS(UNIX) + // srandomdev is not guaranteed to exist on linux so we use this poor seed, this should be improved + timeval time; + gettimeofday(&time, 0); + srandom(static_cast<unsigned>(time.tv_usec * getpid())); +#else + srand(static_cast<unsigned>(time(0))); +#endif + +#if USE(MERSENNE_TWISTER_19937) + // use rand() to initialize the Mersenne Twister random number generator. + unsigned long initializationBuffer[4]; + initializationBuffer[0] = (rand() << 16) | rand(); + initializationBuffer[1] = (rand() << 16) | rand(); + initializationBuffer[2] = (rand() << 16) | rand(); + initializationBuffer[3] = (rand() << 16) | rand(); + init_by_array(initializationBuffer, 4); +#endif +} + +} + +#endif diff --git a/Source/JavaScriptCore/wtf/RedBlackTree.h b/Source/JavaScriptCore/wtf/RedBlackTree.h new file mode 100644 index 000000000..af4e5c88a --- /dev/null +++ b/Source/JavaScriptCore/wtf/RedBlackTree.h @@ -0,0 +1,583 @@ +/* + * Copyright (C) 2010, 2011 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of Apple Computer, Inc. ("Apple") 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 APPLE AND ITS 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 APPLE OR ITS 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. + */ + +#ifndef RedBlackTree_h +#define RedBlackTree_h + +#include <wtf/Assertions.h> +#include <wtf/Noncopyable.h> + +namespace WTF { + +// This implements a red-black tree with the following properties: +// - The allocation of nodes in the tree is entirely up to the user. +// - If you are in possession of a pointer to a node, you can delete +// it from the tree. The tree will subsequently no longer have a +// reference to this node. +// - The key type must implement operator< and ==. + +template<typename KeyType, typename ValueType> +class RedBlackTree { + WTF_MAKE_NONCOPYABLE(RedBlackTree); +private: + enum Color { + Red = 1, + Black + }; + +public: + class Node { + friend class RedBlackTree; + + public: + Node(KeyType key, ValueType value) + : m_key(key) + , m_value(value) + { + } + + const Node* successor() const + { + const Node* x = this; + if (x->right()) + return treeMinimum(x->right()); + const Node* y = x->parent(); + while (y && x == y->right()) { + x = y; + y = y->parent(); + } + return y; + } + + const Node* predecessor() const + { + const Node* x = this; + if (x->left()) + return treeMaximum(x->left()); + const Node* y = x->parent(); + while (y && x == y->left()) { + x = y; + y = y->parent(); + } + return y; + } + + Node* successor() + { + return const_cast<Node*>(const_cast<const Node*>(this)->successor()); + } + + Node* predecessor() + { + return const_cast<Node*>(const_cast<const Node*>(this)->predecessor()); + } + + KeyType m_key; + ValueType m_value; + + private: + void reset() + { + m_left = 0; + m_right = 0; + m_parentAndRed = 1; // initialize to red + } + + // NOTE: these methods should pack the parent and red into a single + // word. But doing so appears to reveal a bug in the compiler. + Node* parent() const + { + return reinterpret_cast<Node*>(m_parentAndRed & ~static_cast<uintptr_t>(1)); + } + + void setParent(Node* newParent) + { + m_parentAndRed = reinterpret_cast<uintptr_t>(newParent) | (m_parentAndRed & 1); + } + + Node* left() const + { + return m_left; + } + + void setLeft(Node* node) + { + m_left = node; + } + + Node* right() const + { + return m_right; + } + + void setRight(Node* node) + { + m_right = node; + } + + Color color() const + { + if (m_parentAndRed & 1) + return Red; + return Black; + } + + void setColor(Color value) + { + if (value == Red) + m_parentAndRed |= 1; + else + m_parentAndRed &= ~static_cast<uintptr_t>(1); + } + + Node* m_left; + Node* m_right; + uintptr_t m_parentAndRed; + }; + + RedBlackTree() + : m_root(0) + { + } + + void insert(Node* x) + { + x->reset(); + treeInsert(x); + x->setColor(Red); + + while (x != m_root && x->parent()->color() == Red) { + if (x->parent() == x->parent()->parent()->left()) { + Node* y = x->parent()->parent()->right(); + if (y && y->color() == Red) { + // Case 1 + x->parent()->setColor(Black); + y->setColor(Black); + x->parent()->parent()->setColor(Red); + x = x->parent()->parent(); + } else { + if (x == x->parent()->right()) { + // Case 2 + x = x->parent(); + leftRotate(x); + } + // Case 3 + x->parent()->setColor(Black); + x->parent()->parent()->setColor(Red); + rightRotate(x->parent()->parent()); + } + } else { + // Same as "then" clause with "right" and "left" exchanged. + Node* y = x->parent()->parent()->left(); + if (y && y->color() == Red) { + // Case 1 + x->parent()->setColor(Black); + y->setColor(Black); + x->parent()->parent()->setColor(Red); + x = x->parent()->parent(); + } else { + if (x == x->parent()->left()) { + // Case 2 + x = x->parent(); + rightRotate(x); + } + // Case 3 + x->parent()->setColor(Black); + x->parent()->parent()->setColor(Red); + leftRotate(x->parent()->parent()); + } + } + } + + m_root->setColor(Black); + } + + Node* remove(Node* z) + { + ASSERT(z); + ASSERT(z->parent() || z == m_root); + + // Y is the node to be unlinked from the tree. + Node* y; + if (!z->left() || !z->right()) + y = z; + else + y = z->successor(); + + // Y is guaranteed to be non-null at this point. + Node* x; + if (y->left()) + x = y->left(); + else + x = y->right(); + + // X is the child of y which might potentially replace y in + // the tree. X might be null at this point. + Node* xParent; + if (x) { + x->setParent(y->parent()); + xParent = x->parent(); + } else + xParent = y->parent(); + if (!y->parent()) + m_root = x; + else { + if (y == y->parent()->left()) + y->parent()->setLeft(x); + else + y->parent()->setRight(x); + } + + if (y != z) { + if (y->color() == Black) + removeFixup(x, xParent); + + y->setParent(z->parent()); + y->setColor(z->color()); + y->setLeft(z->left()); + y->setRight(z->right()); + + if (z->left()) + z->left()->setParent(y); + if (z->right()) + z->right()->setParent(y); + if (z->parent()) { + if (z->parent()->left() == z) + z->parent()->setLeft(y); + else + z->parent()->setRight(y); + } else { + ASSERT(m_root == z); + m_root = y; + } + } else if (y->color() == Black) + removeFixup(x, xParent); + + return z; + } + + Node* remove(const KeyType& key) + { + Node* result = findExact(key); + if (!result) + return 0; + return remove(result); + } + + Node* findExact(const KeyType& key) const + { + for (Node* current = m_root; current;) { + if (current->m_key == key) + return current; + if (key < current->m_key) + current = current->left(); + else + current = current->right(); + } + return 0; + } + + Node* findLeastGreaterThanOrEqual(const KeyType& key) const + { + Node* best = 0; + for (Node* current = m_root; current;) { + if (current->m_key == key) + return current; + if (current->m_key < key) + current = current->right(); + else { + best = current; + current = current->left(); + } + } + return best; + } + + Node* findGreatestLessThanOrEqual(const KeyType& key) const + { + Node* best = 0; + for (Node* current = m_root; current;) { + if (current->m_key == key) + return current; + if (current->m_key > key) + current = current->left(); + else { + best = current; + current = current->right(); + } + } + return best; + } + + Node* first() const + { + if (!m_root) + return 0; + return treeMinimum(m_root); + } + + Node* last() const + { + if (!m_root) + return 0; + return treeMaximum(m_root); + } + + // This is an O(n) operation. + size_t size() + { + size_t result = 0; + for (Node* current = first(); current; current = current->successor()) + result++; + return result; + } + + // This is an O(1) operation. + bool isEmpty() + { + return !m_root; + } + +private: + // Finds the minimum element in the sub-tree rooted at the given + // node. + static Node* treeMinimum(Node* x) + { + while (x->left()) + x = x->left(); + return x; + } + + static Node* treeMaximum(Node* x) + { + while (x->right()) + x = x->right(); + return x; + } + + static const Node* treeMinimum(const Node* x) + { + while (x->left()) + x = x->left(); + return x; + } + + static const Node* treeMaximum(const Node* x) + { + while (x->right()) + x = x->right(); + return x; + } + + void treeInsert(Node* z) + { + ASSERT(!z->left()); + ASSERT(!z->right()); + ASSERT(!z->parent()); + ASSERT(z->color() == Red); + + Node* y = 0; + Node* x = m_root; + while (x) { + y = x; + if (z->m_key < x->m_key) + x = x->left(); + else + x = x->right(); + } + z->setParent(y); + if (!y) + m_root = z; + else { + if (z->m_key < y->m_key) + y->setLeft(z); + else + y->setRight(z); + } + } + + //---------------------------------------------------------------------- + // Red-Black tree operations + // + + // Left-rotates the subtree rooted at x. + // Returns the new root of the subtree (x's right child). + Node* leftRotate(Node* x) + { + // Set y. + Node* y = x->right(); + + // Turn y's left subtree into x's right subtree. + x->setRight(y->left()); + if (y->left()) + y->left()->setParent(x); + + // Link x's parent to y. + y->setParent(x->parent()); + if (!x->parent()) + m_root = y; + else { + if (x == x->parent()->left()) + x->parent()->setLeft(y); + else + x->parent()->setRight(y); + } + + // Put x on y's left. + y->setLeft(x); + x->setParent(y); + + return y; + } + + // Right-rotates the subtree rooted at y. + // Returns the new root of the subtree (y's left child). + Node* rightRotate(Node* y) + { + // Set x. + Node* x = y->left(); + + // Turn x's right subtree into y's left subtree. + y->setLeft(x->right()); + if (x->right()) + x->right()->setParent(y); + + // Link y's parent to x. + x->setParent(y->parent()); + if (!y->parent()) + m_root = x; + else { + if (y == y->parent()->left()) + y->parent()->setLeft(x); + else + y->parent()->setRight(x); + } + + // Put y on x's right. + x->setRight(y); + y->setParent(x); + + return x; + } + + // Restores the red-black property to the tree after splicing out + // a node. Note that x may be null, which is why xParent must be + // supplied. + void removeFixup(Node* x, Node* xParent) + { + while (x != m_root && (!x || x->color() == Black)) { + if (x == xParent->left()) { + // Note: the text points out that w can not be null. + // The reason is not obvious from simply looking at + // the code; it comes about from the properties of the + // red-black tree. + Node* w = xParent->right(); + ASSERT(w); // x's sibling should not be null. + if (w->color() == Red) { + // Case 1 + w->setColor(Black); + xParent->setColor(Red); + leftRotate(xParent); + w = xParent->right(); + } + if ((!w->left() || w->left()->color() == Black) + && (!w->right() || w->right()->color() == Black)) { + // Case 2 + w->setColor(Red); + x = xParent; + xParent = x->parent(); + } else { + if (!w->right() || w->right()->color() == Black) { + // Case 3 + w->left()->setColor(Black); + w->setColor(Red); + rightRotate(w); + w = xParent->right(); + } + // Case 4 + w->setColor(xParent->color()); + xParent->setColor(Black); + if (w->right()) + w->right()->setColor(Black); + leftRotate(xParent); + x = m_root; + xParent = x->parent(); + } + } else { + // Same as "then" clause with "right" and "left" + // exchanged. + + // Note: the text points out that w can not be null. + // The reason is not obvious from simply looking at + // the code; it comes about from the properties of the + // red-black tree. + Node* w = xParent->left(); + ASSERT(w); // x's sibling should not be null. + if (w->color() == Red) { + // Case 1 + w->setColor(Black); + xParent->setColor(Red); + rightRotate(xParent); + w = xParent->left(); + } + if ((!w->right() || w->right()->color() == Black) + && (!w->left() || w->left()->color() == Black)) { + // Case 2 + w->setColor(Red); + x = xParent; + xParent = x->parent(); + } else { + if (!w->left() || w->left()->color() == Black) { + // Case 3 + w->right()->setColor(Black); + w->setColor(Red); + leftRotate(w); + w = xParent->left(); + } + // Case 4 + w->setColor(xParent->color()); + xParent->setColor(Black); + if (w->left()) + w->left()->setColor(Black); + rightRotate(xParent); + x = m_root; + xParent = x->parent(); + } + } + } + if (x) + x->setColor(Black); + } + + Node* m_root; +}; + +} + +#endif + diff --git a/Source/JavaScriptCore/wtf/RefCounted.h b/Source/JavaScriptCore/wtf/RefCounted.h new file mode 100644 index 000000000..51c5dc26e --- /dev/null +++ b/Source/JavaScriptCore/wtf/RefCounted.h @@ -0,0 +1,234 @@ +/* + * Copyright (C) 2006, 2007, 2008, 2009, 2010 Apple Inc. All rights reserved. + * + * This library 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 library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#ifndef RefCounted_h +#define RefCounted_h + +#include "Assertions.h" +#include "FastAllocBase.h" +#include "ThreadRestrictionVerifier.h" +#include "Noncopyable.h" +#include "OwnPtr.h" +#include "UnusedParam.h" + +namespace WTF { + +// This base class holds the non-template methods and attributes. +// The RefCounted class inherits from it reducing the template bloat +// generated by the compiler (technique called template hoisting). +class RefCountedBase { +public: + void ref() + { +#ifndef NDEBUG + // Start thread verification as soon as the ref count gets to 2. This + // heuristic reflects the fact that items are often created on one thread + // and then given to another thread to be used. + // FIXME: Make this restriction tigher. Especially as we move to more + // common methods for sharing items across threads like CrossThreadCopier.h + // We should be able to add a "detachFromThread" method to make this explicit. + if (m_refCount == 1) + m_verifier.setShared(true); +#endif + // If this assert fires, it either indicates a thread safety issue or + // that the verification needs to change. See ThreadRestrictionVerifier for + // the different modes. + ASSERT(m_verifier.isSafeToUse()); + ASSERT(!m_deletionHasBegun); + ASSERT(!m_adoptionIsRequired); + ++m_refCount; + } + + bool hasOneRef() const + { + ASSERT(m_verifier.isSafeToUse()); + ASSERT(!m_deletionHasBegun); + return m_refCount == 1; + } + + int refCount() const + { + ASSERT(m_verifier.isSafeToUse()); + return m_refCount; + } + + void setMutexForVerifier(Mutex&); + +#if HAVE(DISPATCH_H) + void setDispatchQueueForVerifier(dispatch_queue_t); +#endif + + // Turns off verification. Use of this method is discouraged (instead extend + // ThreadRestrictionVerifier to verify your case). + // FIXME: remove this method. + void deprecatedTurnOffVerifier() + { +#ifndef NDEBUG + m_verifier.turnOffVerification(); +#endif + } + + void relaxAdoptionRequirement() + { +#ifndef NDEBUG + ASSERT(!m_deletionHasBegun); + ASSERT(m_adoptionIsRequired); + m_adoptionIsRequired = false; +#endif + } + + // Helper for generating JIT code. Please do not use for non-JIT purposes. + const int* addressOfCount() const + { + return &m_refCount; + } + +protected: + RefCountedBase() + : m_refCount(1) +#ifndef NDEBUG + , m_deletionHasBegun(false) + , m_adoptionIsRequired(true) +#endif + { + } + + ~RefCountedBase() + { + ASSERT(m_deletionHasBegun); + ASSERT(!m_adoptionIsRequired); + } + + // Returns whether the pointer should be freed or not. + bool derefBase() + { + ASSERT(m_verifier.isSafeToUse()); + ASSERT(!m_deletionHasBegun); + ASSERT(!m_adoptionIsRequired); + + ASSERT(m_refCount > 0); + if (m_refCount == 1) { +#ifndef NDEBUG + m_deletionHasBegun = true; +#endif + return true; + } + + --m_refCount; +#ifndef NDEBUG + // Stop thread verification when the ref goes to 1 because it + // is safe to be passed to another thread at this point. + if (m_refCount == 1) + m_verifier.setShared(false); +#endif + return false; + } + +#ifndef NDEBUG + bool deletionHasBegun() const + { + return m_deletionHasBegun; + } +#endif + +private: + +#ifndef NDEBUG + friend void adopted(RefCountedBase*); +#endif + + int m_refCount; +#ifndef NDEBUG + bool m_deletionHasBegun; + bool m_adoptionIsRequired; + ThreadRestrictionVerifier m_verifier; +#endif +}; + +#ifndef NDEBUG + +inline void adopted(RefCountedBase* object) +{ + if (!object) + return; + ASSERT(!object->m_deletionHasBegun); + object->m_adoptionIsRequired = false; +} + +#endif + +template<typename T> class RefCounted : public RefCountedBase { + WTF_MAKE_NONCOPYABLE(RefCounted); WTF_MAKE_FAST_ALLOCATED; +public: + void deref() + { + if (derefBase()) + delete static_cast<T*>(this); + } + +protected: + RefCounted() { } + ~RefCounted() + { + } +}; + +template<typename T> class RefCountedCustomAllocated : public RefCountedBase { + WTF_MAKE_NONCOPYABLE(RefCountedCustomAllocated); + +public: + void deref() + { + if (derefBase()) + delete static_cast<T*>(this); + } + +protected: + ~RefCountedCustomAllocated() + { + } +}; + +#ifdef NDEBUG +inline void RefCountedBase::setMutexForVerifier(Mutex&) { } +#else +inline void RefCountedBase::setMutexForVerifier(Mutex& mutex) +{ + m_verifier.setMutexMode(mutex); +} +#endif + +#if HAVE(DISPATCH_H) +#ifdef NDEBUG +inline void RefCountedBase::setDispatchQueueForVerifier(dispatch_queue_t) { } +#else +inline void RefCountedBase::setDispatchQueueForVerifier(dispatch_queue_t queue) +{ + m_verifier.setDispatchQueueMode(queue); +} +#endif // NDEBUG +#endif // HAVE(DISPATCH_H) + +} // namespace WTF + +using WTF::RefCounted; +using WTF::RefCountedCustomAllocated; + +#endif // RefCounted_h diff --git a/Source/JavaScriptCore/wtf/RefCountedLeakCounter.cpp b/Source/JavaScriptCore/wtf/RefCountedLeakCounter.cpp new file mode 100644 index 000000000..670f2a0ce --- /dev/null +++ b/Source/JavaScriptCore/wtf/RefCountedLeakCounter.cpp @@ -0,0 +1,92 @@ +/* + * Copyright (C) 2008 Apple Inc. All rights reserved. + * + * This library 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 library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#include "config.h" +#include "RefCountedLeakCounter.h" + +#include <wtf/HashCountedSet.h> + +namespace WTF { + +#ifdef NDEBUG + +void RefCountedLeakCounter::suppressMessages(const char*) { } +void RefCountedLeakCounter::cancelMessageSuppression(const char*) { } + +RefCountedLeakCounter::RefCountedLeakCounter(const char*) { } +RefCountedLeakCounter::~RefCountedLeakCounter() { } + +void RefCountedLeakCounter::increment() { } +void RefCountedLeakCounter::decrement() { } + +#else + +#define LOG_CHANNEL_PREFIX Log +static WTFLogChannel LogRefCountedLeaks = { 0x00000000, "", WTFLogChannelOn }; + +typedef HashCountedSet<const char*, PtrHash<const char*> > ReasonSet; +static ReasonSet* leakMessageSuppressionReasons; + +void RefCountedLeakCounter::suppressMessages(const char* reason) +{ + if (!leakMessageSuppressionReasons) + leakMessageSuppressionReasons = new ReasonSet; + leakMessageSuppressionReasons->add(reason); +} + +void RefCountedLeakCounter::cancelMessageSuppression(const char* reason) +{ + ASSERT(leakMessageSuppressionReasons); + ASSERT(leakMessageSuppressionReasons->contains(reason)); + leakMessageSuppressionReasons->remove(reason); +} + +RefCountedLeakCounter::RefCountedLeakCounter(const char* description) + : m_description(description) +{ +} + +RefCountedLeakCounter::~RefCountedLeakCounter() +{ + static bool loggedSuppressionReason; + if (m_count) { + if (!leakMessageSuppressionReasons || leakMessageSuppressionReasons->isEmpty()) + LOG(RefCountedLeaks, "LEAK: %u %s", m_count, m_description); + else if (!loggedSuppressionReason) { + // This logs only one reason. Later we could change it so we log all the reasons. + LOG(RefCountedLeaks, "No leak checking done: %s", leakMessageSuppressionReasons->begin()->first); + loggedSuppressionReason = true; + } + } +} + +void RefCountedLeakCounter::increment() +{ + atomicIncrement(&m_count); +} + +void RefCountedLeakCounter::decrement() +{ + atomicDecrement(&m_count); +} + +#endif + +} // namespace WTF diff --git a/Source/JavaScriptCore/wtf/RefCountedLeakCounter.h b/Source/JavaScriptCore/wtf/RefCountedLeakCounter.h new file mode 100644 index 000000000..22c7506ac --- /dev/null +++ b/Source/JavaScriptCore/wtf/RefCountedLeakCounter.h @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2008 Apple Inc. All rights reserved. + * + * This library 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 library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#ifndef RefCountedLeakCounter_h +#define RefCountedLeakCounter_h + +#include "Assertions.h" +#include "Threading.h" + +namespace WTF { + + struct RefCountedLeakCounter { + static void suppressMessages(const char*); + static void cancelMessageSuppression(const char*); + + explicit RefCountedLeakCounter(const char* description); + ~RefCountedLeakCounter(); + + void increment(); + void decrement(); + +#ifndef NDEBUG + private: +#if COMPILER(MINGW) || COMPILER(MSVC7_OR_LOWER) || OS(WINCE) + int m_count; +#else + volatile int m_count; +#endif + const char* m_description; +#endif + }; + +} // namespace WTF + +#endif diff --git a/Source/JavaScriptCore/wtf/RefPtr.h b/Source/JavaScriptCore/wtf/RefPtr.h new file mode 100644 index 000000000..ac94993e3 --- /dev/null +++ b/Source/JavaScriptCore/wtf/RefPtr.h @@ -0,0 +1,232 @@ +/* + * Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010 Apple Inc. All rights reserved. + * + * This library 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 library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +// RefPtr and PassRefPtr are documented at http://webkit.org/coding/RefPtr.html + +#ifndef WTF_RefPtr_h +#define WTF_RefPtr_h + +#include <algorithm> +#include "FastAllocBase.h" +#include "PassRefPtr.h" + +namespace WTF { + + enum PlacementNewAdoptType { PlacementNewAdopt }; + + template<typename T> class PassRefPtr; + template<typename T> class NonNullPassRefPtr; + + enum HashTableDeletedValueType { HashTableDeletedValue }; + + template<typename T> class RefPtr { + WTF_MAKE_FAST_ALLOCATED; + public: + ALWAYS_INLINE RefPtr() : m_ptr(0) { } + ALWAYS_INLINE RefPtr(T* ptr) : m_ptr(ptr) { refIfNotNull(ptr); } + ALWAYS_INLINE RefPtr(const RefPtr& o) : m_ptr(o.m_ptr) { refIfNotNull(m_ptr); } + template<typename U> RefPtr(const RefPtr<U>& o) : m_ptr(o.get()) { refIfNotNull(m_ptr); } + + // See comments in PassRefPtr.h for an explanation of why these takes const references. + template<typename U> RefPtr(const PassRefPtr<U>&); + template<typename U> RefPtr(const NonNullPassRefPtr<U>&); + + // Special constructor for cases where we overwrite an object in place. + ALWAYS_INLINE RefPtr(PlacementNewAdoptType) { } + + // Hash table deleted values, which are only constructed and never copied or destroyed. + RefPtr(HashTableDeletedValueType) : m_ptr(hashTableDeletedValue()) { } + bool isHashTableDeletedValue() const { return m_ptr == hashTableDeletedValue(); } + + ALWAYS_INLINE ~RefPtr() { derefIfNotNull(m_ptr); } + + T* get() const { return m_ptr; } + + void clear(); + PassRefPtr<T> release() { PassRefPtr<T> tmp = adoptRef(m_ptr); m_ptr = 0; return tmp; } + + T& operator*() const { return *m_ptr; } + ALWAYS_INLINE T* operator->() const { return m_ptr; } + + bool operator!() const { return !m_ptr; } + + // This conversion operator allows implicit conversion to bool but not to other integer types. + typedef T* (RefPtr::*UnspecifiedBoolType); + operator UnspecifiedBoolType() const { return m_ptr ? &RefPtr::m_ptr : 0; } + + RefPtr& operator=(const RefPtr&); + RefPtr& operator=(T*); + RefPtr& operator=(const PassRefPtr<T>&); + RefPtr& operator=(const NonNullPassRefPtr<T>&); +#if !COMPILER_SUPPORTS(CXX_NULLPTR) + RefPtr& operator=(std::nullptr_t) { clear(); return *this; } +#endif + template<typename U> RefPtr& operator=(const RefPtr<U>&); + template<typename U> RefPtr& operator=(const PassRefPtr<U>&); + template<typename U> RefPtr& operator=(const NonNullPassRefPtr<U>&); + + void swap(RefPtr&); + + static T* hashTableDeletedValue() { return reinterpret_cast<T*>(-1); } + + private: + T* m_ptr; + }; + + template<typename T> template<typename U> inline RefPtr<T>::RefPtr(const PassRefPtr<U>& o) + : m_ptr(o.leakRef()) + { + } + + template<typename T> template<typename U> inline RefPtr<T>::RefPtr(const NonNullPassRefPtr<U>& o) + : m_ptr(o.leakRef()) + { + } + + template<typename T> inline void RefPtr<T>::clear() + { + T* ptr = m_ptr; + m_ptr = 0; + derefIfNotNull(ptr); + } + + template<typename T> inline RefPtr<T>& RefPtr<T>::operator=(const RefPtr<T>& o) + { + T* optr = o.get(); + refIfNotNull(optr); + T* ptr = m_ptr; + m_ptr = optr; + derefIfNotNull(ptr); + return *this; + } + + template<typename T> template<typename U> inline RefPtr<T>& RefPtr<T>::operator=(const RefPtr<U>& o) + { + T* optr = o.get(); + refIfNotNull(optr); + T* ptr = m_ptr; + m_ptr = optr; + derefIfNotNull(ptr); + return *this; + } + + template<typename T> inline RefPtr<T>& RefPtr<T>::operator=(T* optr) + { + refIfNotNull(optr); + T* ptr = m_ptr; + m_ptr = optr; + derefIfNotNull(ptr); + return *this; + } + + template<typename T> inline RefPtr<T>& RefPtr<T>::operator=(const PassRefPtr<T>& o) + { + T* ptr = m_ptr; + m_ptr = o.leakRef(); + derefIfNotNull(ptr); + return *this; + } + + template<typename T> inline RefPtr<T>& RefPtr<T>::operator=(const NonNullPassRefPtr<T>& o) + { + T* ptr = m_ptr; + m_ptr = o.leakRef(); + derefIfNotNull(ptr); + return *this; + } + + template<typename T> template<typename U> inline RefPtr<T>& RefPtr<T>::operator=(const PassRefPtr<U>& o) + { + T* ptr = m_ptr; + m_ptr = o.leakRef(); + derefIfNotNull(ptr); + return *this; + } + + template<typename T> template<typename U> inline RefPtr<T>& RefPtr<T>::operator=(const NonNullPassRefPtr<U>& o) + { + T* ptr = m_ptr; + m_ptr = o.leakRef(); + derefIfNotNull(ptr); + return *this; + } + + template<class T> inline void RefPtr<T>::swap(RefPtr<T>& o) + { + std::swap(m_ptr, o.m_ptr); + } + + template<class T> inline void swap(RefPtr<T>& a, RefPtr<T>& b) + { + a.swap(b); + } + + template<typename T, typename U> inline bool operator==(const RefPtr<T>& a, const RefPtr<U>& b) + { + return a.get() == b.get(); + } + + template<typename T, typename U> inline bool operator==(const RefPtr<T>& a, U* b) + { + return a.get() == b; + } + + template<typename T, typename U> inline bool operator==(T* a, const RefPtr<U>& b) + { + return a == b.get(); + } + + template<typename T, typename U> inline bool operator!=(const RefPtr<T>& a, const RefPtr<U>& b) + { + return a.get() != b.get(); + } + + template<typename T, typename U> inline bool operator!=(const RefPtr<T>& a, U* b) + { + return a.get() != b; + } + + template<typename T, typename U> inline bool operator!=(T* a, const RefPtr<U>& b) + { + return a != b.get(); + } + + template<typename T, typename U> inline RefPtr<T> static_pointer_cast(const RefPtr<U>& p) + { + return RefPtr<T>(static_cast<T*>(p.get())); + } + + template<typename T, typename U> inline RefPtr<T> const_pointer_cast(const RefPtr<U>& p) + { + return RefPtr<T>(const_cast<T*>(p.get())); + } + + template<typename T> inline T* getPtr(const RefPtr<T>& p) + { + return p.get(); + } + +} // namespace WTF + +using WTF::RefPtr; +using WTF::static_pointer_cast; +using WTF::const_pointer_cast; + +#endif // WTF_RefPtr_h diff --git a/Source/JavaScriptCore/wtf/RefPtrHashMap.h b/Source/JavaScriptCore/wtf/RefPtrHashMap.h new file mode 100644 index 000000000..f48a59cf6 --- /dev/null +++ b/Source/JavaScriptCore/wtf/RefPtrHashMap.h @@ -0,0 +1,329 @@ +/* + * Copyright (C) 2005, 2006, 2007, 2008, 2011 Apple Inc. All rights reserved. + * + * This library 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 library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#ifndef RefPtrHashMap_h +#define RefPtrHashMap_h + +namespace WTF { + + // This specialization is a copy of HashMap for use with RefPtr keys, with overloaded functions + // to allow for lookup by pointer instead of RefPtr, avoiding ref-count churn. + + // FIXME: Find a way to do this with traits that doesn't require a copy of the HashMap template. + + template<typename T, typename MappedArg, typename HashArg, typename KeyTraitsArg, typename MappedTraitsArg> + class HashMap<RefPtr<T>, MappedArg, HashArg, KeyTraitsArg, MappedTraitsArg> { + WTF_MAKE_FAST_ALLOCATED; + private: + typedef KeyTraitsArg KeyTraits; + typedef MappedTraitsArg MappedTraits; + typedef PairHashTraits<KeyTraits, MappedTraits> ValueTraits; + + public: + typedef typename KeyTraits::TraitType KeyType; + typedef T* RawKeyType; + typedef typename MappedTraits::TraitType MappedType; + typedef typename ValueTraits::TraitType ValueType; + + private: + typedef typename MappedTraits::PassInType MappedPassInType; + typedef typename MappedTraits::PassOutType MappedPassOutType; + typedef typename MappedTraits::PeekType MappedPeekType; + + typedef typename ReferenceTypeMaker<MappedPassInType>::ReferenceType MappedPassInReferenceType; + + typedef HashArg HashFunctions; + + typedef HashTable<KeyType, ValueType, PairFirstExtractor<ValueType>, + HashFunctions, ValueTraits, KeyTraits> HashTableType; + + typedef HashMapTranslator<ValueTraits, HashFunctions> + Translator; + + public: + typedef HashTableIteratorAdapter<HashTableType, ValueType> iterator; + typedef HashTableConstIteratorAdapter<HashTableType, ValueType> const_iterator; + + void swap(HashMap&); + + int size() const; + int capacity() const; + bool isEmpty() const; + + // iterators iterate over pairs of keys and values + iterator begin(); + iterator end(); + const_iterator begin() const; + const_iterator end() const; + + iterator find(const KeyType&); + iterator find(RawKeyType); + const_iterator find(const KeyType&) const; + const_iterator find(RawKeyType) const; + bool contains(const KeyType&) const; + bool contains(RawKeyType) const; + MappedPeekType get(const KeyType&) const; + MappedPeekType get(RawKeyType) const; + MappedPeekType inlineGet(RawKeyType) const; + + // replaces value but not key if key is already present + // return value is a pair of the iterator to the key location, + // and a boolean that's true if a new value was actually added + pair<iterator, bool> set(const KeyType&, MappedPassInType); + pair<iterator, bool> set(RawKeyType, MappedPassInType); + + // does nothing if key is already present + // return value is a pair of the iterator to the key location, + // and a boolean that's true if a new value was actually added + pair<iterator, bool> add(const KeyType&, MappedPassInType); + pair<iterator, bool> add(RawKeyType, MappedPassInType); + + void remove(const KeyType&); + void remove(RawKeyType); + void remove(iterator); + void clear(); + + MappedPassOutType take(const KeyType&); // efficient combination of get with remove + MappedPassOutType take(RawKeyType); // efficient combination of get with remove + + private: + pair<iterator, bool> inlineAdd(const KeyType&, MappedPassInReferenceType); + pair<iterator, bool> inlineAdd(RawKeyType, MappedPassInReferenceType); + + HashTableType m_impl; + }; + + template<typename T, typename U, typename V, typename W, typename X> + inline void HashMap<RefPtr<T>, U, V, W, X>::swap(HashMap& other) + { + m_impl.swap(other.m_impl); + } + + template<typename T, typename U, typename V, typename W, typename X> + inline int HashMap<RefPtr<T>, U, V, W, X>::size() const + { + return m_impl.size(); + } + + template<typename T, typename U, typename V, typename W, typename X> + inline int HashMap<RefPtr<T>, U, V, W, X>::capacity() const + { + return m_impl.capacity(); + } + + template<typename T, typename U, typename V, typename W, typename X> + inline bool HashMap<RefPtr<T>, U, V, W, X>::isEmpty() const + { + return m_impl.isEmpty(); + } + + template<typename T, typename U, typename V, typename W, typename X> + inline typename HashMap<RefPtr<T>, U, V, W, X>::iterator HashMap<RefPtr<T>, U, V, W, X>::begin() + { + return m_impl.begin(); + } + + template<typename T, typename U, typename V, typename W, typename X> + inline typename HashMap<RefPtr<T>, U, V, W, X>::iterator HashMap<RefPtr<T>, U, V, W, X>::end() + { + return m_impl.end(); + } + + template<typename T, typename U, typename V, typename W, typename X> + inline typename HashMap<RefPtr<T>, U, V, W, X>::const_iterator HashMap<RefPtr<T>, U, V, W, X>::begin() const + { + return m_impl.begin(); + } + + template<typename T, typename U, typename V, typename W, typename X> + inline typename HashMap<RefPtr<T>, U, V, W, X>::const_iterator HashMap<RefPtr<T>, U, V, W, X>::end() const + { + return m_impl.end(); + } + + template<typename T, typename U, typename V, typename W, typename X> + inline typename HashMap<RefPtr<T>, U, V, W, X>::iterator HashMap<RefPtr<T>, U, V, W, X>::find(const KeyType& key) + { + return m_impl.find(key); + } + + template<typename T, typename U, typename V, typename W, typename X> + inline typename HashMap<RefPtr<T>, U, V, W, X>::iterator HashMap<RefPtr<T>, U, V, W, X>::find(RawKeyType key) + { + return m_impl.template find<Translator>(key); + } + + template<typename T, typename U, typename V, typename W, typename X> + inline typename HashMap<RefPtr<T>, U, V, W, X>::const_iterator HashMap<RefPtr<T>, U, V, W, X>::find(const KeyType& key) const + { + return m_impl.find(key); + } + + template<typename T, typename U, typename V, typename W, typename X> + inline typename HashMap<RefPtr<T>, U, V, W, X>::const_iterator HashMap<RefPtr<T>, U, V, W, X>::find(RawKeyType key) const + { + return m_impl.template find<Translator>(key); + } + + template<typename T, typename U, typename V, typename W, typename X> + inline bool HashMap<RefPtr<T>, U, V, W, X>::contains(const KeyType& key) const + { + return m_impl.contains(key); + } + + template<typename T, typename U, typename V, typename W, typename X> + inline bool HashMap<RefPtr<T>, U, V, W, X>::contains(RawKeyType key) const + { + return m_impl.template contains<Translator>(key); + } + + template<typename T, typename U, typename V, typename W, typename X> + inline pair<typename HashMap<RefPtr<T>, U, V, W, X>::iterator, bool> + HashMap<RefPtr<T>, U, V, W, X>::inlineAdd(const KeyType& key, MappedPassInReferenceType mapped) + { + return m_impl.template add<Translator>(key, mapped); + } + + template<typename T, typename U, typename V, typename W, typename X> + inline pair<typename HashMap<RefPtr<T>, U, V, W, X>::iterator, bool> + HashMap<RefPtr<T>, U, V, W, X>::inlineAdd(RawKeyType key, MappedPassInReferenceType mapped) + { + return m_impl.template add<Translator>(key, mapped); + } + + template<typename T, typename U, typename V, typename W, typename X> + pair<typename HashMap<RefPtr<T>, U, V, W, X>::iterator, bool> + HashMap<RefPtr<T>, U, V, W, X>::set(const KeyType& key, MappedPassInType mapped) + { + pair<iterator, bool> result = inlineAdd(key, mapped); + if (!result.second) { + // The inlineAdd call above found an existing hash table entry; we need to set the mapped value. + MappedTraits::store(mapped, result.first->second); + } + return result; + } + + template<typename T, typename U, typename V, typename W, typename X> + pair<typename HashMap<RefPtr<T>, U, V, W, X>::iterator, bool> + HashMap<RefPtr<T>, U, V, W, X>::set(RawKeyType key, MappedPassInType mapped) + { + pair<iterator, bool> result = inlineAdd(key, mapped); + if (!result.second) { + // The inlineAdd call above found an existing hash table entry; we need to set the mapped value. + MappedTraits::store(mapped, result.first->second); + } + return result; + } + + template<typename T, typename U, typename V, typename W, typename X> + pair<typename HashMap<RefPtr<T>, U, V, W, X>::iterator, bool> + HashMap<RefPtr<T>, U, V, W, X>::add(const KeyType& key, MappedPassInType mapped) + { + return inlineAdd(key, mapped); + } + + template<typename T, typename U, typename V, typename W, typename X> + pair<typename HashMap<RefPtr<T>, U, V, W, X>::iterator, bool> + HashMap<RefPtr<T>, U, V, W, X>::add(RawKeyType key, MappedPassInType mapped) + { + return inlineAdd(key, mapped); + } + + template<typename T, typename U, typename V, typename W, typename MappedTraits> + typename HashMap<RefPtr<T>, U, V, W, MappedTraits>::MappedPeekType + HashMap<RefPtr<T>, U, V, W, MappedTraits>::get(const KeyType& key) const + { + ValueType* entry = const_cast<HashTableType&>(m_impl).lookup(key); + if (!entry) + return MappedTraits::peek(MappedTraits::emptyValue()); + return MappedTraits::peek(entry->second); + } + + template<typename T, typename U, typename V, typename W, typename MappedTraits> + typename HashMap<RefPtr<T>, U, V, W, MappedTraits>::MappedPeekType + inline HashMap<RefPtr<T>, U, V, W, MappedTraits>::inlineGet(RawKeyType key) const + { + ValueType* entry = const_cast<HashTableType&>(m_impl).template lookup<Translator>(key); + if (!entry) + return MappedTraits::peek(MappedTraits::emptyValue()); + return MappedTraits::peek(entry->second); + } + + template<typename T, typename U, typename V, typename W, typename MappedTraits> + typename HashMap<RefPtr<T>, U, V, W, MappedTraits>::MappedPeekType + HashMap<RefPtr<T>, U, V, W, MappedTraits>::get(RawKeyType key) const + { + return inlineGet(key); + } + + template<typename T, typename U, typename V, typename W, typename X> + inline void HashMap<RefPtr<T>, U, V, W, X>::remove(iterator it) + { + if (it.m_impl == m_impl.end()) + return; + m_impl.internalCheckTableConsistency(); + m_impl.removeWithoutEntryConsistencyCheck(it.m_impl); + } + + template<typename T, typename U, typename V, typename W, typename X> + inline void HashMap<RefPtr<T>, U, V, W, X>::remove(const KeyType& key) + { + remove(find(key)); + } + + template<typename T, typename U, typename V, typename W, typename X> + inline void HashMap<RefPtr<T>, U, V, W, X>::remove(RawKeyType key) + { + remove(find(key)); + } + + template<typename T, typename U, typename V, typename W, typename X> + inline void HashMap<RefPtr<T>, U, V, W, X>::clear() + { + m_impl.clear(); + } + + template<typename T, typename U, typename V, typename W, typename MappedTraits> + typename HashMap<RefPtr<T>, U, V, W, MappedTraits>::MappedPassOutType + HashMap<RefPtr<T>, U, V, W, MappedTraits>::take(const KeyType& key) + { + iterator it = find(key); + if (it == end()) + return MappedTraits::passOut(MappedTraits::emptyValue()); + MappedPassOutType result = MappedTraits::passOut(it->second); + remove(it); + return result; + } + + template<typename T, typename U, typename V, typename W, typename MappedTraits> + typename HashMap<RefPtr<T>, U, V, W, MappedTraits>::MappedPassOutType + HashMap<RefPtr<T>, U, V, W, MappedTraits>::take(RawKeyType key) + { + iterator it = find(key); + if (it == end()) + return MappedTraits::passOut(MappedTraits::emptyValue()); + MappedPassOutType result = MappedTraits::passOut(it->second); + remove(it); + return result; + } + +} // namespace WTF + +#endif // RefPtrHashMap_h diff --git a/Source/JavaScriptCore/wtf/RetainPtr.h b/Source/JavaScriptCore/wtf/RetainPtr.h new file mode 100644 index 000000000..3d4b0c3f9 --- /dev/null +++ b/Source/JavaScriptCore/wtf/RetainPtr.h @@ -0,0 +1,299 @@ +/* + * Copyright (C) 2005, 2006, 2007, 2008, 2010 Apple Inc. All rights reserved. + * + * This library 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 library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#ifndef RetainPtr_h +#define RetainPtr_h + +#include "HashTraits.h" +#include "NullPtr.h" +#include "TypeTraits.h" +#include <algorithm> + +#if USE(CF) +#include <CoreFoundation/CoreFoundation.h> +#endif + +#ifdef __OBJC__ +#import <Foundation/Foundation.h> +#endif + +namespace WTF { + + // Unlike most most of our smart pointers, RetainPtr can take either the pointer type or the pointed-to type, + // so both RetainPtr<NSDictionary> and RetainPtr<CFDictionaryRef> will work. + + enum AdoptCFTag { AdoptCF }; + enum AdoptNSTag { AdoptNS }; + +#ifdef __OBJC__ + inline void adoptNSReference(id ptr) + { + if (ptr) { + CFRetain(ptr); + [ptr release]; + } + } +#endif + + template<typename T> class RetainPtr { + public: + typedef typename RemovePointer<T>::Type ValueType; + typedef ValueType* PtrType; + + RetainPtr() : m_ptr(0) {} + RetainPtr(PtrType ptr) : m_ptr(ptr) { if (ptr) CFRetain(ptr); } + + RetainPtr(AdoptCFTag, PtrType ptr) : m_ptr(ptr) { } + RetainPtr(AdoptNSTag, PtrType ptr) : m_ptr(ptr) { adoptNSReference(ptr); } + + RetainPtr(const RetainPtr& o) : m_ptr(o.m_ptr) { if (PtrType ptr = m_ptr) CFRetain(ptr); } + +#if COMPILER_SUPPORTS(CXX_RVALUE_REFERENCES) + RetainPtr(RetainPtr&& o) : m_ptr(o.leakRef()) { } +#endif + + // Hash table deleted values, which are only constructed and never copied or destroyed. + RetainPtr(HashTableDeletedValueType) : m_ptr(hashTableDeletedValue()) { } + bool isHashTableDeletedValue() const { return m_ptr == hashTableDeletedValue(); } + + ~RetainPtr() { if (PtrType ptr = m_ptr) CFRelease(ptr); } + + template<typename U> RetainPtr(const RetainPtr<U>&); + + PtrType get() const { return m_ptr; } + + void clear(); + PtrType leakRef() WARN_UNUSED_RETURN; + + PtrType operator->() const { return m_ptr; } + + bool operator!() const { return !m_ptr; } + + // This conversion operator allows implicit conversion to bool but not to other integer types. + typedef PtrType RetainPtr::*UnspecifiedBoolType; + operator UnspecifiedBoolType() const { return m_ptr ? &RetainPtr::m_ptr : 0; } + + RetainPtr& operator=(const RetainPtr&); + template<typename U> RetainPtr& operator=(const RetainPtr<U>&); + RetainPtr& operator=(PtrType); + template<typename U> RetainPtr& operator=(U*); + +#if COMPILER_SUPPORTS(CXX_RVALUE_REFERENCES) + RetainPtr& operator=(RetainPtr&&); + template<typename U> RetainPtr& operator=(RetainPtr<U>&&); +#endif + +#if !COMPILER_SUPPORTS(CXX_NULLPTR) + RetainPtr& operator=(std::nullptr_t) { clear(); return *this; } +#endif + + void adoptCF(PtrType); + void adoptNS(PtrType); + + void swap(RetainPtr&); + + private: + static PtrType hashTableDeletedValue() { return reinterpret_cast<PtrType>(-1); } + + PtrType m_ptr; + }; + + template<typename T> template<typename U> inline RetainPtr<T>::RetainPtr(const RetainPtr<U>& o) + : m_ptr(o.get()) + { + if (PtrType ptr = m_ptr) + CFRetain(ptr); + } + + template<typename T> inline void RetainPtr<T>::clear() + { + if (PtrType ptr = m_ptr) { + m_ptr = 0; + CFRelease(ptr); + } + } + + template<typename T> inline typename RetainPtr<T>::PtrType RetainPtr<T>::leakRef() + { + PtrType ptr = m_ptr; + m_ptr = 0; + return ptr; + } + + template<typename T> inline RetainPtr<T>& RetainPtr<T>::operator=(const RetainPtr<T>& o) + { + PtrType optr = o.get(); + if (optr) + CFRetain(optr); + PtrType ptr = m_ptr; + m_ptr = optr; + if (ptr) + CFRelease(ptr); + return *this; + } + + template<typename T> template<typename U> inline RetainPtr<T>& RetainPtr<T>::operator=(const RetainPtr<U>& o) + { + PtrType optr = o.get(); + if (optr) + CFRetain(optr); + PtrType ptr = m_ptr; + m_ptr = optr; + if (ptr) + CFRelease(ptr); + return *this; + } + + template<typename T> inline RetainPtr<T>& RetainPtr<T>::operator=(PtrType optr) + { + if (optr) + CFRetain(optr); + PtrType ptr = m_ptr; + m_ptr = optr; + if (ptr) + CFRelease(ptr); + return *this; + } + + template<typename T> template<typename U> inline RetainPtr<T>& RetainPtr<T>::operator=(U* optr) + { + if (optr) + CFRetain(optr); + PtrType ptr = m_ptr; + m_ptr = optr; + if (ptr) + CFRelease(ptr); + return *this; + } + +#if COMPILER_SUPPORTS(CXX_RVALUE_REFERENCES) + template<typename T> inline RetainPtr<T>& RetainPtr<T>::operator=(RetainPtr<T>&& o) + { + adoptCF(o.leakRef()); + return *this; + } + + template<typename T> template<typename U> inline RetainPtr<T>& RetainPtr<T>::operator=(RetainPtr<U>&& o) + { + adoptCF(o.leakRef()); + return *this; + } +#endif + + template<typename T> inline void RetainPtr<T>::adoptCF(PtrType optr) + { + PtrType ptr = m_ptr; + m_ptr = optr; + if (ptr) + CFRelease(ptr); + } + + template<typename T> inline void RetainPtr<T>::adoptNS(PtrType optr) + { + adoptNSReference(optr); + + PtrType ptr = m_ptr; + m_ptr = optr; + if (ptr) + CFRelease(ptr); + } + + template<typename T> inline void RetainPtr<T>::swap(RetainPtr<T>& o) + { + std::swap(m_ptr, o.m_ptr); + } + + template<typename T> inline void swap(RetainPtr<T>& a, RetainPtr<T>& b) + { + a.swap(b); + } + + template<typename T, typename U> inline bool operator==(const RetainPtr<T>& a, const RetainPtr<U>& b) + { + return a.get() == b.get(); + } + + template<typename T, typename U> inline bool operator==(const RetainPtr<T>& a, U* b) + { + return a.get() == b; + } + + template<typename T, typename U> inline bool operator==(T* a, const RetainPtr<U>& b) + { + return a == b.get(); + } + + template<typename T, typename U> inline bool operator!=(const RetainPtr<T>& a, const RetainPtr<U>& b) + { + return a.get() != b.get(); + } + + template<typename T, typename U> inline bool operator!=(const RetainPtr<T>& a, U* b) + { + return a.get() != b; + } + + template<typename T, typename U> inline bool operator!=(T* a, const RetainPtr<U>& b) + { + return a != b.get(); + } + + template<typename T> inline RetainPtr<T> adoptCF(T) WARN_UNUSED_RETURN; + template<typename T> inline RetainPtr<T> adoptCF(T o) + { + return RetainPtr<T>(AdoptCF, o); + } + + template<typename T> inline RetainPtr<T> adoptNS(T) WARN_UNUSED_RETURN; + template<typename T> inline RetainPtr<T> adoptNS(T o) + { + return RetainPtr<T>(AdoptNS, o); + } + + // Helper function for creating a RetainPtr using template argument deduction. + template<typename T> inline RetainPtr<T> retainPtr(T) WARN_UNUSED_RETURN; + template<typename T> inline RetainPtr<T> retainPtr(T o) + { + return RetainPtr<T>(o); + } + + template<typename P> struct HashTraits<RetainPtr<P> > : SimpleClassHashTraits<RetainPtr<P> > { }; + + template<typename P> struct PtrHash<RetainPtr<P> > : PtrHash<typename RetainPtr<P>::PtrType> { + using PtrHash<typename RetainPtr<P>::PtrType>::hash; + static unsigned hash(const RetainPtr<P>& key) { return hash(key.get()); } + using PtrHash<typename RetainPtr<P>::PtrType>::equal; + static bool equal(const RetainPtr<P>& a, const RetainPtr<P>& b) { return a == b; } + static bool equal(typename RetainPtr<P>::PtrType a, const RetainPtr<P>& b) { return a == b; } + static bool equal(const RetainPtr<P>& a, typename RetainPtr<P>::PtrType b) { return a == b; } + }; + + template<typename P> struct DefaultHash<RetainPtr<P> > { typedef PtrHash<RetainPtr<P> > Hash; }; + +} // namespace WTF + +using WTF::AdoptCF; +using WTF::AdoptNS; +using WTF::adoptCF; +using WTF::adoptNS; +using WTF::RetainPtr; +using WTF::retainPtr; + +#endif // WTF_RetainPtr_h diff --git a/Source/JavaScriptCore/wtf/SHA1.cpp b/Source/JavaScriptCore/wtf/SHA1.cpp new file mode 100644 index 000000000..e76f6ac38 --- /dev/null +++ b/Source/JavaScriptCore/wtf/SHA1.cpp @@ -0,0 +1,219 @@ +/* + * Copyright (C) 2011 Google Inc. 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 Google Inc. 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. + */ + +// A straightforward SHA-1 implementation based on RFC 3174. +// http://www.ietf.org/rfc/rfc3174.txt +// The names of functions and variables (such as "a", "b", and "f") follow notations in RFC 3174. + +#include "config.h" +#include "SHA1.h" + +#include "Assertions.h" +#ifndef NDEBUG +#include "StringExtras.h" +#include "text/CString.h" +#endif + +namespace WTF { + +#ifdef NDEBUG +static inline void testSHA1() { } +#else +static bool isTestSHA1Done; + +static void expectSHA1(CString input, int repeat, CString expected) +{ + SHA1 sha1; + for (int i = 0; i < repeat; ++i) + sha1.addBytes(reinterpret_cast<const uint8_t*>(input.data()), input.length()); + Vector<uint8_t, 20> digest; + sha1.computeHash(digest); + char* buffer = 0; + CString actual = CString::newUninitialized(40, buffer); + for (size_t i = 0; i < 20; ++i) { + snprintf(buffer, 3, "%02X", digest.at(i)); + buffer += 2; + } + ASSERT_WITH_MESSAGE(actual == expected, "input: %s, repeat: %d, actual: %s, expected: %s", input.data(), repeat, actual.data(), expected.data()); +} + +static void testSHA1() +{ + if (isTestSHA1Done) + return; + isTestSHA1Done = true; + + // Examples taken from sample code in RFC 3174. + expectSHA1("abc", 1, "A9993E364706816ABA3E25717850C26C9CD0D89D"); + expectSHA1("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq", 1, "84983E441C3BD26EBAAE4AA1F95129E5E54670F1"); + expectSHA1("a", 1000000, "34AA973CD4C4DAA4F61EEB2BDBAD27316534016F"); + expectSHA1("0123456701234567012345670123456701234567012345670123456701234567", 10, "DEA356A2CDDD90C7A7ECEDC5EBB563934F460452"); +} +#endif + +static inline uint32_t f(int t, uint32_t b, uint32_t c, uint32_t d) +{ + ASSERT(t >= 0 && t < 80); + if (t < 20) + return (b & c) | ((~b) & d); + if (t < 40) + return b ^ c ^ d; + if (t < 60) + return (b & c) | (b & d) | (c & d); + return b ^ c ^ d; +} + +static inline uint32_t k(int t) +{ + ASSERT(t >= 0 && t < 80); + if (t < 20) + return 0x5a827999; + if (t < 40) + return 0x6ed9eba1; + if (t < 60) + return 0x8f1bbcdc; + return 0xca62c1d6; +} + +static inline uint32_t rotateLeft(int n, uint32_t x) +{ + ASSERT(n >= 0 && n < 32); + return (x << n) | (x >> (32 - n)); +} + +SHA1::SHA1() +{ + // FIXME: Move unit tests somewhere outside the constructor. See bug 55853. + testSHA1(); + reset(); +} + +void SHA1::addBytes(const uint8_t* input, size_t length) +{ + while (length--) { + ASSERT(m_cursor < 64); + m_buffer[m_cursor++] = *input++; + ++m_totalBytes; + if (m_cursor == 64) + processBlock(); + } +} + +void SHA1::computeHash(Vector<uint8_t, 20>& digest) +{ + finalize(); + + digest.clear(); + digest.resize(20); + for (size_t i = 0; i < 5; ++i) { + // Treat hashValue as a big-endian value. + uint32_t hashValue = m_hash[i]; + for (int j = 0; j < 4; ++j) { + digest[4 * i + (3 - j)] = hashValue & 0xFF; + hashValue >>= 8; + } + } + + reset(); +} + +void SHA1::finalize() +{ + ASSERT(m_cursor < 64); + m_buffer[m_cursor++] = 0x80; + if (m_cursor > 56) { + // Pad out to next block. + while (m_cursor < 64) + m_buffer[m_cursor++] = 0x00; + processBlock(); + } + + for (size_t i = m_cursor; i < 56; ++i) + m_buffer[i] = 0x00; + + // Write the length as a big-endian 64-bit value. + uint64_t bits = m_totalBytes * 8; + for (int i = 0; i < 8; ++i) { + m_buffer[56 + (7 - i)] = bits & 0xFF; + bits >>= 8; + } + m_cursor = 64; + processBlock(); +} + +void SHA1::processBlock() +{ + ASSERT(m_cursor == 64); + + uint32_t w[80] = { 0 }; + for (int t = 0; t < 16; ++t) + w[t] = (m_buffer[t * 4] << 24) | (m_buffer[t * 4 + 1] << 16) | (m_buffer[t * 4 + 2] << 8) | m_buffer[t * 4 + 3]; + for (int t = 16; t < 80; ++t) + w[t] = rotateLeft(1, w[t - 3] ^ w[t - 8] ^ w[t - 14] ^ w[t - 16]); + + uint32_t a = m_hash[0]; + uint32_t b = m_hash[1]; + uint32_t c = m_hash[2]; + uint32_t d = m_hash[3]; + uint32_t e = m_hash[4]; + + for (int t = 0; t < 80; ++t) { + uint32_t temp = rotateLeft(5, a) + f(t, b, c, d) + e + w[t] + k(t); + e = d; + d = c; + c = rotateLeft(30, b); + b = a; + a = temp; + } + + m_hash[0] += a; + m_hash[1] += b; + m_hash[2] += c; + m_hash[3] += d; + m_hash[4] += e; + + m_cursor = 0; +} + +void SHA1::reset() +{ + m_cursor = 0; + m_totalBytes = 0; + m_hash[0] = 0x67452301; + m_hash[1] = 0xefcdab89; + m_hash[2] = 0x98badcfe; + m_hash[3] = 0x10325476; + m_hash[4] = 0xc3d2e1f0; + + // Clear the buffer after use in case it's sensitive. + memset(m_buffer, 0, sizeof(m_buffer)); +} + +} // namespace WTF diff --git a/Source/JavaScriptCore/wtf/SHA1.h b/Source/JavaScriptCore/wtf/SHA1.h new file mode 100644 index 000000000..dad6dc867 --- /dev/null +++ b/Source/JavaScriptCore/wtf/SHA1.h @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2011 Google Inc. 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 Google Inc. 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. + */ + +#ifndef WTF_SHA1_h +#define WTF_SHA1_h + +#include <wtf/Vector.h> + +namespace WTF { + +class SHA1 { +public: + SHA1(); + + void addBytes(const Vector<uint8_t>& input) + { + addBytes(input.data(), input.size()); + } + void addBytes(const uint8_t* input, size_t length); + + // computeHash has a side effect of resetting the state of the object. + void computeHash(Vector<uint8_t, 20>&); + +private: + void finalize(); + void processBlock(); + void reset(); + + uint8_t m_buffer[64]; + size_t m_cursor; // Number of bytes filled in m_buffer (0-64). + uint64_t m_totalBytes; // Number of bytes added so far. + uint32_t m_hash[5]; +}; + +} // namespace WTF + +using WTF::SHA1; + +#endif // WTF_SHA1_h diff --git a/Source/JavaScriptCore/wtf/SegmentedVector.h b/Source/JavaScriptCore/wtf/SegmentedVector.h new file mode 100644 index 000000000..cb9a5f3a8 --- /dev/null +++ b/Source/JavaScriptCore/wtf/SegmentedVector.h @@ -0,0 +1,255 @@ +/* + * Copyright (C) 2008 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of Apple Computer, Inc. ("Apple") 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 APPLE AND ITS 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 APPLE OR ITS 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. + */ + +#ifndef SegmentedVector_h +#define SegmentedVector_h + +#include <wtf/Vector.h> + +namespace WTF { + + // An iterator for SegmentedVector. It supports only the pre ++ operator + template <typename T, size_t SegmentSize> class SegmentedVector; + template <typename T, size_t SegmentSize> class SegmentedVectorIterator { + private: + friend class SegmentedVector<T, SegmentSize>; + public: + typedef SegmentedVectorIterator<T, SegmentSize> Iterator; + + ~SegmentedVectorIterator() { } + + T& operator*() const { return m_vector.m_segments.at(m_segment)->at(m_index); } + T* operator->() const { return &m_vector.m_segments.at(m_segment)->at(m_index); } + + // Only prefix ++ operator supported + Iterator& operator++() + { + ASSERT(m_index != SegmentSize); + ++m_index; + if (m_index >= m_vector.m_segments.at(m_segment)->size()) { + if (m_segment + 1 < m_vector.m_segments.size()) { + ASSERT(m_vector.m_segments.at(m_segment)->size() > 0); + ++m_segment; + m_index = 0; + } else { + // Points to the "end" symbol + m_segment = 0; + m_index = SegmentSize; + } + } + return *this; + } + + bool operator==(const Iterator& other) const + { + return m_index == other.m_index && m_segment == other.m_segment && &m_vector == &other.m_vector; + } + + bool operator!=(const Iterator& other) const + { + return m_index != other.m_index || m_segment != other.m_segment || &m_vector != &other.m_vector; + } + + SegmentedVectorIterator& operator=(const SegmentedVectorIterator<T, SegmentSize>& other) + { + m_vector = other.m_vector; + m_segment = other.m_segment; + m_index = other.m_index; + return *this; + } + + private: + SegmentedVectorIterator(SegmentedVector<T, SegmentSize>& vector, size_t segment, size_t index) + : m_vector(vector) + , m_segment(segment) + , m_index(index) + { + } + + SegmentedVector<T, SegmentSize>& m_vector; + size_t m_segment; + size_t m_index; + }; + + // SegmentedVector is just like Vector, but it doesn't move the values + // stored in its buffer when it grows. Therefore, it is safe to keep + // pointers into a SegmentedVector. + template <typename T, size_t SegmentSize> class SegmentedVector { + friend class SegmentedVectorIterator<T, SegmentSize>; + public: + typedef SegmentedVectorIterator<T, SegmentSize> Iterator; + + SegmentedVector() + : m_size(0) + { + m_segments.append(&m_inlineSegment); + } + + ~SegmentedVector() + { + deleteAllSegments(); + } + + size_t size() const { return m_size; } + bool isEmpty() const { return !size(); } + + T& at(size_t index) + { + if (index < SegmentSize) + return m_inlineSegment[index]; + return segmentFor(index)->at(subscriptFor(index)); + } + + T& operator[](size_t index) + { + return at(index); + } + + T& last() + { + return at(size() - 1); + } + + template <typename U> void append(const U& value) + { + ++m_size; + + if (m_size <= SegmentSize) { + m_inlineSegment.uncheckedAppend(value); + return; + } + + if (!segmentExistsFor(m_size - 1)) + m_segments.append(new Segment); + segmentFor(m_size - 1)->uncheckedAppend(value); + } + + T& alloc() + { + append<T>(T()); + return last(); + } + + void removeLast() + { + if (m_size <= SegmentSize) + m_inlineSegment.removeLast(); + else + segmentFor(m_size - 1)->removeLast(); + --m_size; + } + + void grow(size_t size) + { + ASSERT(size > m_size); + ensureSegmentsFor(size); + m_size = size; + } + + void clear() + { + deleteAllSegments(); + m_segments.resize(1); + m_inlineSegment.clear(); + m_size = 0; + } + + Iterator begin() + { + return Iterator(*this, 0, m_size ? 0 : SegmentSize); + } + + Iterator end() + { + return Iterator(*this, 0, SegmentSize); + } + + private: + typedef Vector<T, SegmentSize> Segment; + + void deleteAllSegments() + { + // Skip the first segment, because it's our inline segment, which was + // not created by new. + for (size_t i = 1; i < m_segments.size(); i++) + delete m_segments[i]; + } + + bool segmentExistsFor(size_t index) + { + return index / SegmentSize < m_segments.size(); + } + + Segment* segmentFor(size_t index) + { + return m_segments[index / SegmentSize]; + } + + size_t subscriptFor(size_t index) + { + return index % SegmentSize; + } + + void ensureSegmentsFor(size_t size) + { + size_t segmentCount = m_size / SegmentSize; + if (m_size % SegmentSize) + ++segmentCount; + segmentCount = std::max<size_t>(segmentCount, 1); // We always have at least our inline segment. + + size_t neededSegmentCount = size / SegmentSize; + if (size % SegmentSize) + ++neededSegmentCount; + + // Fill up to N - 1 segments. + size_t end = neededSegmentCount - 1; + for (size_t i = segmentCount - 1; i < end; ++i) + ensureSegment(i, SegmentSize); + + // Grow segment N to accomodate the remainder. + ensureSegment(end, subscriptFor(size - 1) + 1); + } + + void ensureSegment(size_t segmentIndex, size_t size) + { + ASSERT(segmentIndex <= m_segments.size()); + if (segmentIndex == m_segments.size()) + m_segments.append(new Segment); + m_segments[segmentIndex]->grow(size); + } + + size_t m_size; + Segment m_inlineSegment; + Vector<Segment*, 32> m_segments; + }; + +} // namespace WTF + +using WTF::SegmentedVector; + +#endif // SegmentedVector_h diff --git a/Source/JavaScriptCore/wtf/SentinelLinkedList.h b/Source/JavaScriptCore/wtf/SentinelLinkedList.h new file mode 100644 index 000000000..ecd602452 --- /dev/null +++ b/Source/JavaScriptCore/wtf/SentinelLinkedList.h @@ -0,0 +1,159 @@ +/* + * Copyright (C) 2011 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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 APPLE INC. AND ITS 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 APPLE INC. OR ITS 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. + */ + +// A SentinelLinkedList is a linked list with dummy head and tail sentinels, +// which allow for branch-less insertion and removal, and removal without a +// pointer to the list. +// +// Requires: Node is a concrete class with: +// Node(SentinelTag); +// void setPrev(Node*); +// Node* prev(); +// void setNext(Node*); +// Node* next(); + +#ifndef SentinelLinkedList_h +#define SentinelLinkedList_h + +namespace WTF { + +enum SentinelTag { Sentinel }; + +template<typename T> +class BasicRawSentinelNode { +public: + BasicRawSentinelNode(SentinelTag) + : m_next(0) + , m_prev(0) + { + } + + BasicRawSentinelNode() + : m_next(0) + , m_prev(0) + { + } + + void setPrev(BasicRawSentinelNode* prev) { m_prev = prev; } + void setNext(BasicRawSentinelNode* next) { m_next = next; } + + T* prev() { return static_cast<T*>(m_prev); } + T* next() { return static_cast<T*>(m_next); } + + bool isOnList() const + { + ASSERT(!!m_prev == !!m_next); + return !!m_prev; + } + + void remove(); + +private: + BasicRawSentinelNode* m_next; + BasicRawSentinelNode* m_prev; +}; + +template <typename T, typename RawNode = T> class SentinelLinkedList { +public: + typedef T* iterator; + + SentinelLinkedList(); + + void push(T*); + static void remove(T*); + + iterator begin(); + iterator end(); + +private: + RawNode m_headSentinel; + RawNode m_tailSentinel; +}; + +template <typename T> void BasicRawSentinelNode<T>::remove() +{ + SentinelLinkedList<T, BasicRawSentinelNode<T> >::remove(static_cast<T*>(this)); +} + +template <typename T, typename RawNode> inline SentinelLinkedList<T, RawNode>::SentinelLinkedList() + : m_headSentinel(Sentinel) + , m_tailSentinel(Sentinel) +{ + m_headSentinel.setNext(&m_tailSentinel); + m_headSentinel.setPrev(0); + + m_tailSentinel.setPrev(&m_headSentinel); + m_tailSentinel.setNext(0); +} + +template <typename T, typename RawNode> inline typename SentinelLinkedList<T, RawNode>::iterator SentinelLinkedList<T, RawNode>::begin() +{ + return static_cast<T*>(m_headSentinel.next()); +} + +template <typename T, typename RawNode> inline typename SentinelLinkedList<T, RawNode>::iterator SentinelLinkedList<T, RawNode>::end() +{ + return static_cast<T*>(&m_tailSentinel); +} + +template <typename T, typename RawNode> inline void SentinelLinkedList<T, RawNode>::push(T* node) +{ + ASSERT(node); + ASSERT(!node->prev()); + ASSERT(!node->next()); + + RawNode* prev = &m_headSentinel; + RawNode* next = m_headSentinel.next(); + + node->setPrev(prev); + node->setNext(next); + + prev->setNext(node); + next->setPrev(node); +} + +template <typename T, typename RawNode> inline void SentinelLinkedList<T, RawNode>::remove(T* node) +{ + ASSERT(node); + ASSERT(!!node->prev()); + ASSERT(!!node->next()); + + RawNode* prev = node->prev(); + RawNode* next = node->next(); + + prev->setNext(next); + next->setPrev(prev); + + node->setPrev(0); + node->setNext(0); +} + +} + +using WTF::BasicRawSentinelNode; +using WTF::SentinelLinkedList; + +#endif + diff --git a/Source/JavaScriptCore/wtf/SinglyLinkedList.h b/Source/JavaScriptCore/wtf/SinglyLinkedList.h new file mode 100644 index 000000000..c00bf3687 --- /dev/null +++ b/Source/JavaScriptCore/wtf/SinglyLinkedList.h @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2011 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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 APPLE INC. AND ITS 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 APPLE INC. OR ITS 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. + */ + +#ifndef SinglyLinkedList_h +#define SinglyLinkedList_h + +namespace WTF { + +template <typename Node> class SinglyLinkedList { +public: + SinglyLinkedList(); + + bool isEmpty(); + + void push(Node*); + Node* pop(); + +private: + Node* m_head; +}; + +template <typename Node> inline SinglyLinkedList<Node>::SinglyLinkedList() + : m_head(0) +{ +} + +template <typename Node> inline bool SinglyLinkedList<Node>::isEmpty() +{ + return !m_head; +} + +template <typename Node> inline void SinglyLinkedList<Node>::push(Node* node) +{ + ASSERT(node); + node->setNext(m_head); + m_head = node; +} + +template <typename Node> inline Node* SinglyLinkedList<Node>::pop() +{ + Node* tmp = m_head; + m_head = m_head->next(); + return tmp; +} + +} + +using WTF::SinglyLinkedList; + +#endif diff --git a/Source/JavaScriptCore/wtf/SizeLimits.cpp b/Source/JavaScriptCore/wtf/SizeLimits.cpp new file mode 100644 index 000000000..95d9c2b1e --- /dev/null +++ b/Source/JavaScriptCore/wtf/SizeLimits.cpp @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2010 Google Inc. 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 Google Inc. 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 "config.h" + +#include <wtf/Assertions.h> +#include <wtf/OwnPtr.h> +#include <wtf/PassRefPtr.h> +#include <wtf/RefCounted.h> +#include <wtf/RefPtr.h> +#include <wtf/ThreadRestrictionVerifier.h> +#include <wtf/Vector.h> + +namespace WTF { + +#ifndef NDEBUG +struct SameSizeAsRefCounted { + int a; + bool b; + bool c; + ThreadRestrictionVerifier d; + // The debug version may get bigger. +}; +#else +struct SameSizeAsRefCounted { + int a; + // Don't add anything here because this should stay small. +}; +#endif + +COMPILE_ASSERT(sizeof(OwnPtr<int>) == sizeof(int*), OwnPtr_should_stay_small); +COMPILE_ASSERT(sizeof(PassRefPtr<RefCounted<int> >) == sizeof(int*), PassRefPtr_should_stay_small); +COMPILE_ASSERT(sizeof(RefCounted<int>) == sizeof(SameSizeAsRefCounted), RefCounted_should_stay_small); +COMPILE_ASSERT(sizeof(RefCountedCustomAllocated<int>) == sizeof(SameSizeAsRefCounted), RefCountedCustomAllocated_should_stay_small); +COMPILE_ASSERT(sizeof(RefPtr<RefCounted<int> >) == sizeof(int*), RefPtr_should_stay_small); +COMPILE_ASSERT(sizeof(Vector<int>) == 3 * sizeof(int*), Vector_should_stay_small); + +} diff --git a/Source/JavaScriptCore/wtf/Spectrum.h b/Source/JavaScriptCore/wtf/Spectrum.h new file mode 100644 index 000000000..c403eda18 --- /dev/null +++ b/Source/JavaScriptCore/wtf/Spectrum.h @@ -0,0 +1,105 @@ +/* + * Copyright (C) 2011 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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 APPLE INC. ``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 APPLE INC. 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. + */ + +#ifndef Spectrum_h +#define Spectrum_h + +#include "HashMap.h" +#include "Vector.h" +#include <algorithm> + +namespace WTF { + +template<typename T> +class Spectrum { +public: + typedef typename HashMap<T, unsigned long>::iterator iterator; + typedef typename HashMap<T, unsigned long>::const_iterator const_iterator; + + Spectrum() { } + + void add(const T& key, unsigned long count = 1) + { + std::pair<iterator, bool> result = m_map.add(key, count); + if (!result.second) + result.first->second += count; + } + + unsigned long get(const T& key) const + { + const_iterator iter = m_map.find(key); + if (iter == m_map.end()) + return 0; + return iter->second; + } + + iterator begin() { return m_map.begin(); } + iterator end() { return m_map.end(); } + const_iterator begin() const { return m_map.begin(); } + const_iterator end() const { return m_map.end(); } + + struct KeyAndCount { + KeyAndCount() { } + + KeyAndCount(const T& key, unsigned long count) + : key(key) + , count(count) + { + } + + bool operator<(const KeyAndCount& other) const + { + if (count != other.count) + return count < other.count; + // This causes lower-ordered keys being returned first; this is really just + // here to make sure that the order is somewhat deterministic rather than being + // determined by hashing. + return key > other.key; + } + + T key; + unsigned long count; + }; + + // Returns a list ordered from lowest-count to highest-count. + Vector<KeyAndCount> buildList() const + { + Vector<KeyAndCount> list; + for (const_iterator iter = begin(); iter != end(); ++iter) + list.append(KeyAndCount(iter->first, iter->second)); + + std::sort(list.begin(), list.end()); + return list; + } + +private: + HashMap<T, unsigned long> m_map; +}; + +} // namespace WTF + +using WTF::Spectrum; + +#endif // Spectrum_h diff --git a/Source/JavaScriptCore/wtf/StackBounds.cpp b/Source/JavaScriptCore/wtf/StackBounds.cpp new file mode 100644 index 000000000..a272ce3de --- /dev/null +++ b/Source/JavaScriptCore/wtf/StackBounds.cpp @@ -0,0 +1,261 @@ +/* + * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009 Apple Inc. All rights reserved. + * Copyright (C) 2007 Eric Seidel <eric@webkit.org> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#include "config.h" +#include "StackBounds.h" + +#if OS(DARWIN) + +#include <mach/task.h> +#include <mach/thread_act.h> +#include <pthread.h> + +#elif OS(WINDOWS) + +#include <windows.h> + +#elif OS(SOLARIS) + +#include <thread.h> + +#elif OS(QNX) + +#include <errno.h> +#include <fcntl.h> +#include <pthread.h> +#include <stdio.h> +#include <string.h> +#include <sys/procfs.h> + +#elif OS(UNIX) + +#include <pthread.h> +#if HAVE(PTHREAD_NP_H) +#include <pthread_np.h> +#endif + +#endif + +namespace WTF { + +// Bug 26276 - Need a mechanism to determine stack extent +// +// These platforms should now be working correctly: +// DARWIN, QNX, UNIX +// These platforms are not: +// WINDOWS, SOLARIS, OPENBSD, WINCE +// +// FIXME: remove this! - this code unsafely guesses at stack sizes! +#if OS(WINDOWS) || OS(SOLARIS) || OS(OPENBSD) +// Based on the current limit used by the JSC parser, guess the stack size. +static const ptrdiff_t estimatedStackSize = 128 * sizeof(void*) * 1024; +// This method assumes the stack is growing downwards. +static void* estimateStackBound(void* origin) +{ + return static_cast<char*>(origin) - estimatedStackSize; +} +#endif + +#if OS(DARWIN) + +void StackBounds::initialize() +{ + pthread_t thread = pthread_self(); + m_origin = pthread_get_stackaddr_np(thread); + m_bound = static_cast<char*>(m_origin) - pthread_get_stacksize_np(thread); +} + +#elif OS(QNX) + +void StackBounds::initialize() +{ + void* stackBase = 0; + size_t stackSize = 0; + + struct _debug_thread_info threadInfo; + memset(&threadInfo, 0, sizeof(threadInfo)); + threadInfo.tid = pthread_self(); + int fd = open("/proc/self", O_RDONLY); + if (fd == -1) { + LOG_ERROR("Unable to open /proc/self (errno: %d)", errno); + CRASH(); + } + devctl(fd, DCMD_PROC_TIDSTATUS, &threadInfo, sizeof(threadInfo), 0); + close(fd); + stackBase = reinterpret_cast<void*>(threadInfo.stkbase); + stackSize = threadInfo.stksize; + ASSERT(stackBase); + + m_bound = static_cast<char*>(stackBase) + 0x1000; // 4kb guard page + m_origin = static_cast<char*>(stackBase) + stackSize; +} + +#elif OS(SOLARIS) + +void StackBounds::initialize() +{ + stack_t s; + thr_stksegment(&s); + m_origin = s.ss_sp; + m_bound = estimateStackBound(m_origin); +} + +#elif OS(OPENBSD) + +void StackBounds::initialize() +{ + pthread_t thread = pthread_self(); + stack_t stack; + pthread_stackseg_np(thread, &stack); + m_origin = stack.ss_sp; + m_bound = estimateStackBound(m_origin); +} + +#elif OS(UNIX) + +void StackBounds::initialize() +{ + void* stackBase = 0; + size_t stackSize = 0; + + pthread_t thread = pthread_self(); + pthread_attr_t sattr; + pthread_attr_init(&sattr); +#if HAVE(PTHREAD_NP_H) || OS(NETBSD) + // e.g. on FreeBSD 5.4, neundorf@kde.org + pthread_attr_get_np(thread, &sattr); +#else + // FIXME: this function is non-portable; other POSIX systems may have different np alternatives + pthread_getattr_np(thread, &sattr); +#endif + int rc = pthread_attr_getstack(&sattr, &stackBase, &stackSize); + (void)rc; // FIXME: Deal with error code somehow? Seems fatal. + ASSERT(stackBase); + pthread_attr_destroy(&sattr); + m_bound = stackBase; + m_origin = static_cast<char*>(stackBase) + stackSize; +} + +#elif OS(WINCE) + +static bool detectGrowingDownward(void* previousFrame) +{ + // Find the address of this stack frame by taking the address of a local variable. + int thisFrame; + return previousFrame > &thisFrame; +} + +static inline bool isPageWritable(void* page) +{ + MEMORY_BASIC_INFORMATION memoryInformation; + DWORD result = VirtualQuery(page, &memoryInformation, sizeof(memoryInformation)); + + // return false on error, including ptr outside memory + if (result != sizeof(memoryInformation)) + return false; + + DWORD protect = memoryInformation.Protect & ~(PAGE_GUARD | PAGE_NOCACHE); + return protect == PAGE_READWRITE + || protect == PAGE_WRITECOPY + || protect == PAGE_EXECUTE_READWRITE + || protect == PAGE_EXECUTE_WRITECOPY; +} + +static inline void* getLowerStackBound(char* currentPage, DWORD pageSize) +{ + while (currentPage > 0) { + // check for underflow + if (currentPage >= reinterpret_cast<char*>(pageSize)) + currentPage -= pageSize; + else + currentPage = 0; + + if (!isPageWritable(currentPage)) + return currentPage + pageSize; + } + + return 0; +} + +static inline void* getUpperStackBound(char* currentPage, DWORD pageSize) +{ + do { + // guaranteed to complete because isPageWritable returns false at end of memory + currentPage += pageSize; + } while (isPageWritable(currentPage)); + + return currentPage - pageSize; +} + +void StackBounds::initialize() +{ + // find the address of this stack frame by taking the address of a local variable + void* thisFrame = &thisFrame; + bool isGrowingDownward = detectGrowingDownward(thisFrame); + + SYSTEM_INFO systemInfo; + GetSystemInfo(&systemInfo); + DWORD pageSize = systemInfo.dwPageSize; + + // scan all of memory starting from this frame, and return the last writeable page found + char* currentPage = reinterpret_cast<char*>(reinterpret_cast<DWORD>(thisFrame) & ~(pageSize - 1)); + void* lowerStackBound = getLowerStackBound(currentPage, pageSize); + void* upperStackBound = getUpperStackBound(currentPage, pageSize); + + m_origin = isGrowingDownward ? upperStackBound : lowerStackBound; + m_bound = isGrowingDownward ? lowerStackBound : upperStackBound; +} + +#elif OS(WINDOWS) + +void StackBounds::initialize() +{ +#if CPU(X86) && COMPILER(MSVC) + // offset 0x18 from the FS segment register gives a pointer to + // the thread information block for the current thread + NT_TIB* pTib; + __asm { + MOV EAX, FS:[18h] + MOV pTib, EAX + } + m_origin = static_cast<void*>(pTib->StackBase); +#elif CPU(X86) && COMPILER(GCC) + // offset 0x18 from the FS segment register gives a pointer to + // the thread information block for the current thread + NT_TIB* pTib; + asm ( "movl %%fs:0x18, %0\n" + : "=r" (pTib) + ); + m_origin = static_cast<void*>(pTib->StackBase); +#elif CPU(X86_64) + PNT_TIB64 pTib = reinterpret_cast<PNT_TIB64>(NtCurrentTeb()); + m_origin = reinterpret_cast<void*>(pTib->StackBase); +#else +#error Need a way to get the stack bounds on this platform (Windows) +#endif + // Looks like we should be able to get pTib->StackLimit + m_bound = estimateStackBound(m_origin); +} + +#else +#error Need a way to get the stack bounds on this platform +#endif + +} // namespace WTF diff --git a/Source/JavaScriptCore/wtf/StackBounds.h b/Source/JavaScriptCore/wtf/StackBounds.h new file mode 100644 index 000000000..afce8606f --- /dev/null +++ b/Source/JavaScriptCore/wtf/StackBounds.h @@ -0,0 +1,114 @@ +/* + * Copyright (C) 2010 Apple Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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 APPLE COMPUTER, INC. ``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 APPLE COMPUTER, INC. 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. + * + */ + +#ifndef StackBounds_h +#define StackBounds_h + +namespace WTF { + +class StackBounds { + // recursionCheck() / recursionLimit() tests (by default) + // that we are at least this far from the end of the stack. + const static size_t s_defaultAvailabilityDelta = 4096; + +public: + StackBounds() + : m_origin(0) + , m_bound(0) + { + } + + static StackBounds currentThreadStackBounds() + { + StackBounds bounds; + bounds.initialize(); + bounds.checkConsistency(); + return bounds; + } + + void* origin() const + { + ASSERT(m_origin); + return m_origin; + } + + void* current() const + { + checkConsistency(); + void* currentPosition = ¤tPosition; + return currentPosition; + } + + void* recursionLimit(size_t minAvailableDelta = s_defaultAvailabilityDelta) const + { + checkConsistency(); + return isGrowingDownward() + ? static_cast<char*>(m_bound) + minAvailableDelta + : static_cast<char*>(m_bound) - minAvailableDelta; + } + + bool recursionCheck(size_t minAvailableDelta = s_defaultAvailabilityDelta) const + { + checkConsistency(); + return isGrowingDownward() + ? current() >= recursionLimit(minAvailableDelta) + : current() <= recursionLimit(minAvailableDelta); + } + +private: + void initialize(); + + + bool isGrowingDownward() const + { + ASSERT(m_origin && m_bound); +#if OS(WINCE) + return m_origin > m_bound; +#else + return true; +#endif + } + + void checkConsistency() const + { +#if !ASSERT_DISABLED + void* currentPosition = ¤tPosition; + ASSERT(m_origin != m_bound); + ASSERT(isGrowingDownward() + ? (currentPosition < m_origin && currentPosition > m_bound) + : (currentPosition > m_origin && currentPosition < m_bound)); +#endif + } + + void* m_origin; + void* m_bound; +}; + +} // namespace WTF + +using WTF::StackBounds; + +#endif diff --git a/Source/JavaScriptCore/wtf/StaticConstructors.h b/Source/JavaScriptCore/wtf/StaticConstructors.h new file mode 100644 index 000000000..702c0ca5c --- /dev/null +++ b/Source/JavaScriptCore/wtf/StaticConstructors.h @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2006 Apple Computer, Inc. + * + * This library 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 library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#ifndef StaticConstructors_h +#define StaticConstructors_h + +// We need to avoid having static constructors. We achieve this +// with two separate methods for GCC and MSVC. Both methods prevent the static +// initializers from being registered and called on program startup. On GCC, we +// declare the global objects with a different type that can be POD default +// initialized by the linker/loader. On MSVC we use a special compiler feature +// to have the CRT ignore our static initializers. The constructors will never +// be called and the objects will be left uninitialized. +// +// With both of these approaches, we must define and explicitly call an init +// routine that uses placement new to create the objects and overwrite the +// uninitialized placeholders. +// +// This is not completely portable, but is what we have for now without +// changing how a lot of code accesses these global objects. + +#ifdef SKIP_STATIC_CONSTRUCTORS_ON_MSVC +// - Assume that all includes of this header want ALL of their static +// initializers ignored. This is currently the case. This means that if +// a .cc includes this header (or it somehow gets included), all static +// initializers after the include will not be executed. +// - We do this with a pragma, so that all of the static initializer pointers +// go into our own section, and the CRT won't call them. Eventually it would +// be nice if the section was discarded, because we don't want the pointers. +// See: http://msdn.microsoft.com/en-us/library/7977wcck(VS.80).aspx +#pragma warning(disable:4075) +#pragma init_seg(".unwantedstaticinits") +#endif + +#ifndef SKIP_STATIC_CONSTRUCTORS_ON_GCC + // Define an global in the normal way. +#if COMPILER(MSVC7_OR_LOWER) +#define DEFINE_GLOBAL(type, name) \ + const type name; +#else +#define DEFINE_GLOBAL(type, name, ...) \ + const type name; +#endif + +#else +// Define an correctly-sized array of pointers to avoid static initialization. +// Use an array of pointers instead of an array of char in case there is some alignment issue. +#if COMPILER(MSVC7_OR_LOWER) +#define DEFINE_GLOBAL(type, name) \ + void * name[(sizeof(type) + sizeof(void *) - 1) / sizeof(void *)]; +#else +#define DEFINE_GLOBAL(type, name, ...) \ + void * name[(sizeof(type) + sizeof(void *) - 1) / sizeof(void *)]; +#endif +#endif + +#endif // StaticConstructors_h diff --git a/Source/JavaScriptCore/wtf/StdLibExtras.h b/Source/JavaScriptCore/wtf/StdLibExtras.h new file mode 100644 index 000000000..3f99c4d3f --- /dev/null +++ b/Source/JavaScriptCore/wtf/StdLibExtras.h @@ -0,0 +1,290 @@ +/* + * Copyright (C) 2008 Apple Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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 APPLE INC. ``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 APPLE INC. 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. + */ + +#ifndef WTF_StdLibExtras_h +#define WTF_StdLibExtras_h + +#include "CheckedArithmetic.h" +#include "Assertions.h" + +// Use these to declare and define a static local variable (static T;) so that +// it is leaked so that its destructors are not called at exit. Using this +// macro also allows workarounds a compiler bug present in Apple's version of GCC 4.0.1. +#ifndef DEFINE_STATIC_LOCAL +#if COMPILER(GCC) && defined(__APPLE_CC__) && __GNUC__ == 4 && __GNUC_MINOR__ == 0 && __GNUC_PATCHLEVEL__ == 1 +#define DEFINE_STATIC_LOCAL(type, name, arguments) \ + static type* name##Ptr = new type arguments; \ + type& name = *name##Ptr +#else +#define DEFINE_STATIC_LOCAL(type, name, arguments) \ + static type& name = *new type arguments +#endif +#endif + +// Use this macro to declare and define a debug-only global variable that may have a +// non-trivial constructor and destructor. When building with clang, this will suppress +// warnings about global constructors and exit-time destructors. +#ifndef NDEBUG +#if COMPILER(CLANG) +#define DEFINE_DEBUG_ONLY_GLOBAL(type, name, arguments) \ + _Pragma("clang diagnostic push") \ + _Pragma("clang diagnostic ignored \"-Wglobal-constructors\"") \ + _Pragma("clang diagnostic ignored \"-Wexit-time-destructors\"") \ + static type name arguments; \ + _Pragma("clang diagnostic pop") +#else +#define DEFINE_DEBUG_ONLY_GLOBAL(type, name, arguments) \ + static type name arguments; +#endif // COMPILER(CLANG) +#else +#define DEFINE_DEBUG_ONLY_GLOBAL(type, name, arguments) +#endif // NDEBUG + +// OBJECT_OFFSETOF: Like the C++ offsetof macro, but you can use it with classes. +// The magic number 0x4000 is insignificant. We use it to avoid using NULL, since +// NULL can cause compiler problems, especially in cases of multiple inheritance. +#define OBJECT_OFFSETOF(class, field) (reinterpret_cast<ptrdiff_t>(&(reinterpret_cast<class*>(0x4000)->field)) - 0x4000) + +// STRINGIZE: Can convert any value to quoted string, even expandable macros +#define STRINGIZE(exp) #exp +#define STRINGIZE_VALUE_OF(exp) STRINGIZE(exp) + +/* + * The reinterpret_cast<Type1*>([pointer to Type2]) expressions - where + * sizeof(Type1) > sizeof(Type2) - cause the following warning on ARM with GCC: + * increases required alignment of target type. + * + * An implicit or an extra static_cast<void*> bypasses the warning. + * For more info see the following bugzilla entries: + * - https://bugs.webkit.org/show_bug.cgi?id=38045 + * - http://gcc.gnu.org/bugzilla/show_bug.cgi?id=43976 + */ +#if (CPU(ARM) || CPU(MIPS)) && COMPILER(GCC) +template<typename Type> +bool isPointerTypeAlignmentOkay(Type* ptr) +{ + return !(reinterpret_cast<intptr_t>(ptr) % __alignof__(Type)); +} + +template<typename TypePtr> +TypePtr reinterpret_cast_ptr(void* ptr) +{ + ASSERT(isPointerTypeAlignmentOkay(reinterpret_cast<TypePtr>(ptr))); + return reinterpret_cast<TypePtr>(ptr); +} + +template<typename TypePtr> +TypePtr reinterpret_cast_ptr(const void* ptr) +{ + ASSERT(isPointerTypeAlignmentOkay(reinterpret_cast<TypePtr>(ptr))); + return reinterpret_cast<TypePtr>(ptr); +} +#else +#define reinterpret_cast_ptr reinterpret_cast +#endif + +namespace WTF { + +/* + * C++'s idea of a reinterpret_cast lacks sufficient cojones. + */ +template<typename TO, typename FROM> +inline TO bitwise_cast(FROM from) +{ + COMPILE_ASSERT(sizeof(TO) == sizeof(FROM), WTF_bitwise_cast_sizeof_casted_types_is_equal); + union { + FROM from; + TO to; + } u; + u.from = from; + return u.to; +} + +template<typename To, typename From> +inline To safeCast(From value) +{ + ASSERT(isInBounds<To>(value)); + return static_cast<To>(value); +} + +// Returns a count of the number of bits set in 'bits'. +inline size_t bitCount(unsigned bits) +{ + bits = bits - ((bits >> 1) & 0x55555555); + bits = (bits & 0x33333333) + ((bits >> 2) & 0x33333333); + return (((bits + (bits >> 4)) & 0xF0F0F0F) * 0x1010101) >> 24; +} + +// Macro that returns a compile time constant with the length of an array, but gives an error if passed a non-array. +template<typename T, size_t Size> char (&ArrayLengthHelperFunction(T (&)[Size]))[Size]; +#define WTF_ARRAY_LENGTH(array) sizeof(::WTF::ArrayLengthHelperFunction(array)) + +// Efficient implementation that takes advantage of powers of two. +template<size_t divisor> inline size_t roundUpToMultipleOf(size_t x) +{ + COMPILE_ASSERT(divisor && !(divisor & (divisor - 1)), divisor_is_a_power_of_two); + + size_t remainderMask = divisor - 1; + return (x + remainderMask) & ~remainderMask; +} + +enum BinarySearchMode { + KeyMustBePresentInArray, + KeyMustNotBePresentInArray +}; + +// Binary search algorithm, calls extractKey on pre-sorted elements in array, +// compares result with key (KeyTypes should be comparable with '--', '<', '>'). +template<typename ArrayElementType, typename KeyType, KeyType(*extractKey)(ArrayElementType*)> +inline ArrayElementType* binarySearch(ArrayElementType* array, size_t size, KeyType key, BinarySearchMode mode = KeyMustBePresentInArray) +{ + // The array must contain at least one element (pre-condition, array does contain key). + // If the array contains only one element, no need to do the comparison. + while (size > 1) { + // Pick an element to check, half way through the array, and read the value. + int pos = (size - 1) >> 1; + KeyType val = extractKey(&array[pos]); + + // If the key matches, success! + if (val == key) + return &array[pos]; + // The item we are looking for is smaller than the item being check; reduce the value of 'size', + // chopping off the right hand half of the array. + else if (key < val) + size = pos; + // Discard all values in the left hand half of the array, up to and including the item at pos. + else { + size -= (pos + 1); + array += (pos + 1); + } + + // In case of BinarySearchMode = KeyMustBePresentInArray 'size' should never reach zero. + if (mode == KeyMustBePresentInArray) + ASSERT(size); + } + + // In case of BinarySearchMode = KeyMustBePresentInArray if we reach this point + // we've chopped down to one element, no need to check it matches + if (mode == KeyMustBePresentInArray) { + ASSERT(size == 1); + ASSERT(key == extractKey(&array[0])); + } + + return &array[0]; +} + +// Modified binary search algorithm that uses a functor. Note that this is strictly +// more powerful than the above, but results in somewhat less template specialization. +// Hence, depending on inlining heuristics, it might be slower. +template<typename ArrayElementType, typename KeyType, typename ExtractKey> +inline ArrayElementType* binarySearchWithFunctor(ArrayElementType* array, size_t size, KeyType key, BinarySearchMode mode = KeyMustBePresentInArray, const ExtractKey& extractKey = ExtractKey()) +{ + // The array must contain at least one element (pre-condition, array does contain key). + // If the array contains only one element, no need to do the comparison. + while (size > 1) { + // Pick an element to check, half way through the array, and read the value. + int pos = (size - 1) >> 1; + KeyType val = extractKey(&array[pos]); + + // If the key matches, success! + if (val == key) + return &array[pos]; + // The item we are looking for is smaller than the item being check; reduce the value of 'size', + // chopping off the right hand half of the array. + else if (key < val) + size = pos; + // Discard all values in the left hand half of the array, up to and including the item at pos. + else { + size -= (pos + 1); + array += (pos + 1); + } + + // In case of BinarySearchMode = KeyMustBePresentInArray 'size' should never reach zero. + if (mode == KeyMustBePresentInArray) + ASSERT(size); + } + + // In case of BinarySearchMode = KeyMustBePresentInArray if we reach this point + // we've chopped down to one element, no need to check it matches + if (mode == KeyMustBePresentInArray) { + ASSERT(size == 1); + ASSERT(key == extractKey(&array[0])); + } + + return &array[0]; +} + +// Modified binarySearch() algorithm designed for array-like classes that support +// operator[] but not operator+=. One example of a class that qualifies is +// SegmentedVector. +template<typename ArrayElementType, typename KeyType, KeyType(*extractKey)(ArrayElementType*), typename ArrayType> +inline ArrayElementType* genericBinarySearch(ArrayType& array, size_t size, KeyType key) +{ + // The array must contain at least one element (pre-condition, array does conatin key). + // If the array only contains one element, no need to do the comparison. + size_t offset = 0; + while (size > 1) { + // Pick an element to check, half way through the array, and read the value. + int pos = (size - 1) >> 1; + KeyType val = extractKey(&array[offset + pos]); + + // If the key matches, success! + if (val == key) + return &array[offset + pos]; + // The item we are looking for is smaller than the item being check; reduce the value of 'size', + // chopping off the right hand half of the array. + if (key < val) + size = pos; + // Discard all values in the left hand half of the array, up to and including the item at pos. + else { + size -= (pos + 1); + offset += (pos + 1); + } + + // 'size' should never reach zero. + ASSERT(size); + } + + // If we reach this point we've chopped down to one element, no need to check it matches + ASSERT(size == 1); + ASSERT(key == extractKey(&array[offset])); + return &array[offset]; +} + +} // namespace WTF + +// This version of placement new omits a 0 check. +enum NotNullTag { NotNull }; +inline void* operator new(size_t, NotNullTag, void* location) +{ + ASSERT(location); + return location; +} + +using WTF::binarySearch; +using WTF::bitwise_cast; +using WTF::safeCast; + +#endif // WTF_StdLibExtras_h diff --git a/Source/JavaScriptCore/wtf/StringExtras.cpp b/Source/JavaScriptCore/wtf/StringExtras.cpp new file mode 100644 index 000000000..1b96417c8 --- /dev/null +++ b/Source/JavaScriptCore/wtf/StringExtras.cpp @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2009 Company 100, Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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 APPLE COMPUTER, INC. ``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 APPLE COMPUTER, INC. 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 "config.h" + +#if COMPILER(RVCT) && __ARMCC_VERSION < 400000 + +#include "StringExtras.h" + +#include "ASCIICType.h" + +int strcasecmp(const char* s1, const char* s2) +{ + while (toASCIIUpper(*s1) == toASCIIUpper(*s2)) { + if (*s1 == '\0') + return 0; + s1++; + s2++; + } + + return toASCIIUpper(*s1) - toASCIIUpper(*s2); +} + +int strncasecmp(const char* s1, const char* s2, size_t len) +{ + while (len > 0 && toASCIIUpper(*s1) == toASCIIUpper(*s2)) { + if (*s1 == '\0') + return 0; + s1++; + s2++; + len--; + } + + if (!len) + return 0; + + return toASCIIUpper(*s1) - toASCIIUpper(*s2); +} + +#endif diff --git a/Source/JavaScriptCore/wtf/StringExtras.h b/Source/JavaScriptCore/wtf/StringExtras.h new file mode 100644 index 000000000..371e33bf9 --- /dev/null +++ b/Source/JavaScriptCore/wtf/StringExtras.h @@ -0,0 +1,126 @@ +/* + * Copyright (C) 2006, 2010 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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 APPLE COMPUTER, INC. ``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 APPLE COMPUTER, INC. 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. + */ + +#ifndef WTF_StringExtras_h +#define WTF_StringExtras_h + +#include <stdarg.h> +#include <stdio.h> +#include <string.h> + +#if HAVE(STRINGS_H) +#include <strings.h> +#endif + +#if COMPILER(MSVC) +// FIXME: why a COMPILER check instead of OS? also, these should be HAVE checks + +inline int snprintf(char* buffer, size_t count, const char* format, ...) +{ + int result; + va_list args; + va_start(args, format); + result = _vsnprintf(buffer, count, format, args); + va_end(args); + + // In the case where the string entirely filled the buffer, _vsnprintf will not + // null-terminate it, but snprintf must. + if (count > 0) + buffer[count - 1] = '\0'; + + return result; +} + +inline double wtf_vsnprintf(char* buffer, size_t count, const char* format, va_list args) +{ + int result = _vsnprintf(buffer, count, format, args); + + // In the case where the string entirely filled the buffer, _vsnprintf will not + // null-terminate it, but vsnprintf must. + if (count > 0) + buffer[count - 1] = '\0'; + + return result; +} + +// Work around a difference in Microsoft's implementation of vsnprintf, where +// vsnprintf does not null terminate the buffer. WebKit can rely on the null termination. +#define vsnprintf(buffer, count, format, args) wtf_vsnprintf(buffer, count, format, args) + +#if OS(WINCE) + +inline int strnicmp(const char* string1, const char* string2, size_t count) +{ + return _strnicmp(string1, string2, count); +} + +inline int stricmp(const char* string1, const char* string2) +{ + return _stricmp(string1, string2); +} + +inline char* strdup(const char* strSource) +{ + return _strdup(strSource); +} + +#endif + +inline int strncasecmp(const char* s1, const char* s2, size_t len) +{ + return _strnicmp(s1, s2, len); +} + +inline int strcasecmp(const char* s1, const char* s2) +{ + return _stricmp(s1, s2); +} + +#endif + +#if !HAVE(STRNSTR) + +inline char* strnstr(const char* buffer, const char* target, size_t bufferLength) +{ + size_t targetLength = strlen(target); + if (targetLength == 0) + return const_cast<char*>(buffer); + for (const char* start = buffer; *start && start + targetLength <= buffer + bufferLength; start++) { + if (*start == *target && strncmp(start + 1, target + 1, targetLength - 1) == 0) + return const_cast<char*>(start); + } + return 0; +} + +#endif + +#if COMPILER(RVCT) && __ARMCC_VERSION < 400000 + +int strcasecmp(const char* s1, const char* s2); +int strncasecmp(const char* s1, const char* s2, size_t len); + +#endif + +#endif // WTF_StringExtras_h diff --git a/Source/JavaScriptCore/wtf/StringHasher.h b/Source/JavaScriptCore/wtf/StringHasher.h new file mode 100644 index 000000000..714525188 --- /dev/null +++ b/Source/JavaScriptCore/wtf/StringHasher.h @@ -0,0 +1,185 @@ +/* + * Copyright (C) 2005, 2006, 2008, 2010 Apple Inc. All rights reserved. + * Copyright (C) 2010 Patrick Gansterer <paroga@paroga.com> + * + * This library 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 library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ +#ifndef WTF_StringHasher_h +#define WTF_StringHasher_h + +#include <wtf/unicode/Unicode.h> + +namespace WTF { + +// Golden ratio - arbitrary start value to avoid mapping all 0's to all 0's +static const unsigned stringHashingStartValue = 0x9e3779b9U; + +// Paul Hsieh's SuperFastHash +// http://www.azillionmonkeys.com/qed/hash.html +// char* data is interpreted as latin-encoded (zero extended to 16 bits). + +// NOTE: This class must stay in sync with the create_hash_table script in +// JavaScriptCore and the CodeGeneratorJS.pm script in WebCore. +class StringHasher { +public: + static const unsigned flagCount = 8; // Save 8 bits for StringImpl to use as flags. + + inline StringHasher() + : m_hash(stringHashingStartValue) + , m_hasPendingCharacter(false) + , m_pendingCharacter(0) + { + } + + inline void addCharacters(UChar a, UChar b) + { + ASSERT(!m_hasPendingCharacter); + addCharactersToHash(a, b); + } + + inline void addCharacter(UChar ch) + { + if (m_hasPendingCharacter) { + addCharactersToHash(m_pendingCharacter, ch); + m_hasPendingCharacter = false; + return; + } + + m_pendingCharacter = ch; + m_hasPendingCharacter = true; + } + + inline unsigned hash() const + { + unsigned result = m_hash; + + // Handle end case. + if (m_hasPendingCharacter) { + result += m_pendingCharacter; + result ^= result << 11; + result += result >> 17; + } + + // Force "avalanching" of final 31 bits. + result ^= result << 3; + result += result >> 5; + result ^= result << 2; + result += result >> 15; + result ^= result << 10; + + // Reserving space from the high bits for flags preserves most of the hash's + // value, since hash lookup typically masks out the high bits anyway. + result &= (1u << (sizeof(result) * 8 - flagCount)) - 1; + + // This avoids ever returning a hash code of 0, since that is used to + // signal "hash not computed yet". Setting the high bit maintains + // reasonable fidelity to a hash code of 0 because it is likely to yield + // exactly 0 when hash lookup masks out the high bits. + if (!result) + result = 0x80000000 >> flagCount; + + return result; + } + + template<typename T, UChar Converter(T)> static inline unsigned computeHash(const T* data, unsigned length) + { + StringHasher hasher; + bool rem = length & 1; + length >>= 1; + + while (length--) { + hasher.addCharacters(Converter(data[0]), Converter(data[1])); + data += 2; + } + + if (rem) + hasher.addCharacter(Converter(*data)); + + return hasher.hash(); + } + + template<typename T, UChar Converter(T)> static inline unsigned computeHash(const T* data) + { + StringHasher hasher; + + while (true) { + UChar b0 = Converter(*data++); + if (!b0) + break; + UChar b1 = Converter(*data++); + if (!b1) { + hasher.addCharacter(b0); + break; + } + + hasher.addCharacters(b0, b1); + } + + return hasher.hash(); + } + + template<typename T> static inline unsigned computeHash(const T* data, unsigned length) + { + return computeHash<T, defaultConverter>(data, length); + } + + template<typename T> static inline unsigned computeHash(const T* data) + { + return computeHash<T, defaultConverter>(data); + } + + template<size_t length> static inline unsigned hashMemory(const void* data) + { + COMPILE_ASSERT(!(length % 4), length_must_be_a_multible_of_four); + return computeHash<UChar>(static_cast<const UChar*>(data), length / sizeof(UChar)); + } + + static inline unsigned hashMemory(const void* data, unsigned size) + { + ASSERT(!(size % 2)); + return computeHash<UChar>(static_cast<const UChar*>(data), size / sizeof(UChar)); + } + +private: + static inline UChar defaultConverter(UChar ch) + { + return ch; + } + + static inline UChar defaultConverter(LChar ch) + { + return ch; + } + + inline void addCharactersToHash(UChar a, UChar b) + { + m_hash += a; + unsigned tmp = (b << 11) ^ m_hash; + m_hash = (m_hash << 16) ^ tmp; + m_hash += m_hash >> 11; + } + + unsigned m_hash; + bool m_hasPendingCharacter; + UChar m_pendingCharacter; +}; + +} // namespace WTF + +using WTF::StringHasher; + +#endif // WTF_StringHasher_h diff --git a/Source/JavaScriptCore/wtf/TCPackedCache.h b/Source/JavaScriptCore/wtf/TCPackedCache.h new file mode 100644 index 000000000..0464f8fdc --- /dev/null +++ b/Source/JavaScriptCore/wtf/TCPackedCache.h @@ -0,0 +1,234 @@ +// Copyright (c) 2007, Google Inc. +// 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 Google Inc. 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. + +// --- +// Author: Geoff Pike +// +// This file provides a minimal cache that can hold a <key, value> pair +// with little if any wasted space. The types of the key and value +// must be unsigned integral types or at least have unsigned semantics +// for >>, casting, and similar operations. +// +// Synchronization is not provided. However, the cache is implemented +// as an array of cache entries whose type is chosen at compile time. +// If a[i] is atomic on your hardware for the chosen array type then +// raciness will not necessarily lead to bugginess. The cache entries +// must be large enough to hold a partial key and a value packed +// together. The partial keys are bit strings of length +// kKeybits - kHashbits, and the values are bit strings of length kValuebits. +// +// In an effort to use minimal space, every cache entry represents +// some <key, value> pair; the class provides no way to mark a cache +// entry as empty or uninitialized. In practice, you may want to have +// reserved keys or values to get around this limitation. For example, in +// tcmalloc's PageID-to-sizeclass cache, a value of 0 is used as +// "unknown sizeclass." +// +// Usage Considerations +// -------------------- +// +// kHashbits controls the size of the cache. The best value for +// kHashbits will of course depend on the application. Perhaps try +// tuning the value of kHashbits by measuring different values on your +// favorite benchmark. Also remember not to be a pig; other +// programs that need resources may suffer if you are. +// +// The main uses for this class will be when performance is +// critical and there's a convenient type to hold the cache's +// entries. As described above, the number of bits required +// for a cache entry is (kKeybits - kHashbits) + kValuebits. Suppose +// kKeybits + kValuebits is 43. Then it probably makes sense to +// chose kHashbits >= 11 so that cache entries fit in a uint32. +// +// On the other hand, suppose kKeybits = kValuebits = 64. Then +// using this class may be less worthwhile. You'll probably +// be using 128 bits for each entry anyway, so maybe just pick +// a hash function, H, and use an array indexed by H(key): +// void Put(K key, V value) { a_[H(key)] = pair<K, V>(key, value); } +// V GetOrDefault(K key, V default) { const pair<K, V> &p = a_[H(key)]; ... } +// etc. +// +// Further Details +// --------------- +// +// For caches used only by one thread, the following is true: +// 1. For a cache c, +// (c.Put(key, value), c.GetOrDefault(key, 0)) == value +// and +// (c.Put(key, value), <...>, c.GetOrDefault(key, 0)) == value +// if the elided code contains no c.Put calls. +// +// 2. Has(key) will return false if no <key, value> pair with that key +// has ever been Put. However, a newly initialized cache will have +// some <key, value> pairs already present. When you create a new +// cache, you must specify an "initial value." The initialization +// procedure is equivalent to Clear(initial_value), which is +// equivalent to Put(k, initial_value) for all keys k from 0 to +// 2^kHashbits - 1. +// +// 3. If key and key' differ then the only way Put(key, value) may +// cause Has(key') to change is that Has(key') may change from true to +// false. Furthermore, a Put() call that doesn't change Has(key') +// doesn't change GetOrDefault(key', ...) either. +// +// Implementation details: +// +// This is a direct-mapped cache with 2^kHashbits entries; +// the hash function simply takes the low bits of the key. +// So, we don't have to store the low bits of the key in the entries. +// Instead, an entry is the high bits of a key and a value, packed +// together. E.g., a 20 bit key and a 7 bit value only require +// a uint16 for each entry if kHashbits >= 11. +// +// Alternatives to this scheme will be added as needed. + +#ifndef TCMALLOC_PACKED_CACHE_INL_H__ +#define TCMALLOC_PACKED_CACHE_INL_H__ + +#ifndef WTF_CHANGES +#include "base/basictypes.h" // for COMPILE_ASSERT +#include "base/logging.h" // for DCHECK +#endif + +#ifndef DCHECK_EQ +#define DCHECK_EQ(val1, val2) ASSERT((val1) == (val2)) +#endif + +// A safe way of doing "(1 << n) - 1" -- without worrying about overflow +// Note this will all be resolved to a constant expression at compile-time +#define N_ONES_(IntType, N) \ + ( (N) == 0 ? 0 : ((static_cast<IntType>(1) << ((N)-1))-1 + \ + (static_cast<IntType>(1) << ((N)-1))) ) + +// The types K and V provide upper bounds on the number of valid keys +// and values, but we explicitly require the keys to be less than +// 2^kKeybits and the values to be less than 2^kValuebits. The size of +// the table is controlled by kHashbits, and the type of each entry in +// the cache is T. See also the big comment at the top of the file. +template <int kKeybits, typename T> +class PackedCache { + public: + typedef uintptr_t K; + typedef size_t V; + static const size_t kHashbits = 12; + static const size_t kValuebits = 8; + + explicit PackedCache(V initial_value) { + COMPILE_ASSERT(kKeybits <= sizeof(K) * 8, key_size); + COMPILE_ASSERT(kValuebits <= sizeof(V) * 8, value_size); + COMPILE_ASSERT(kHashbits <= kKeybits, hash_function); + COMPILE_ASSERT(kKeybits - kHashbits + kValuebits <= kTbits, + entry_size_must_be_big_enough); + Clear(initial_value); + } + + void Put(K key, V value) { + DCHECK_EQ(key, key & kKeyMask); + DCHECK_EQ(value, value & kValueMask); + array_[Hash(key)] = static_cast<T>(KeyToUpper(key) | value); + } + + bool Has(K key) const { + DCHECK_EQ(key, key & kKeyMask); + return KeyMatch(array_[Hash(key)], key); + } + + V GetOrDefault(K key, V default_value) const { + // As with other code in this class, we touch array_ as few times + // as we can. Assuming entries are read atomically (e.g., their + // type is uintptr_t on most hardware) then certain races are + // harmless. + DCHECK_EQ(key, key & kKeyMask); + T entry = array_[Hash(key)]; + return KeyMatch(entry, key) ? EntryToValue(entry) : default_value; + } + + void Clear(V value) { + DCHECK_EQ(value, value & kValueMask); + for (int i = 0; i < 1 << kHashbits; i++) { + array_[i] = static_cast<T>(value); + } + } + + private: + // We are going to pack a value and the upper part of a key into + // an entry of type T. The UPPER type is for the upper part of a key, + // after the key has been masked and shifted for inclusion in an entry. + typedef T UPPER; + + static V EntryToValue(T t) { return t & kValueMask; } + + static UPPER EntryToUpper(T t) { return t & kUpperMask; } + + // If v is a V and u is an UPPER then you can create an entry by + // doing u | v. kHashbits determines where in a K to find the upper + // part of the key, and kValuebits determines where in the entry to put + // it. + static UPPER KeyToUpper(K k) { + const int shift = kHashbits - kValuebits; + // Assume kHashbits >= kValuebits. It would be easy to lift this assumption. + return static_cast<T>(k >> shift) & kUpperMask; + } + + // This is roughly the inverse of KeyToUpper(). Some of the key has been + // thrown away, since KeyToUpper() masks off the low bits of the key. + static K UpperToPartialKey(UPPER u) { + DCHECK_EQ(u, u & kUpperMask); + const int shift = kHashbits - kValuebits; + // Assume kHashbits >= kValuebits. It would be easy to lift this assumption. + return static_cast<K>(u) << shift; + } + + static size_t Hash(K key) { + return static_cast<size_t>(key) & N_ONES_(size_t, kHashbits); + } + + // Does the entry's partial key match the relevant part of the given key? + static bool KeyMatch(T entry, K key) { + return ((KeyToUpper(key) ^ entry) & kUpperMask) == 0; + } + + static const size_t kTbits = 8 * sizeof(T); + static const int kUpperbits = kKeybits - kHashbits; + + // For masking a K. + static const K kKeyMask = N_ONES_(K, kKeybits); + + // For masking a T. + static const T kUpperMask = N_ONES_(T, kUpperbits) << kValuebits; + + // For masking a V or a T. + static const V kValueMask = N_ONES_(V, kValuebits); + + T array_[1 << kHashbits]; +}; + +#undef N_ONES_ + +#endif // TCMALLOC_PACKED_CACHE_INL_H__ diff --git a/Source/JavaScriptCore/wtf/TCPageMap.h b/Source/JavaScriptCore/wtf/TCPageMap.h new file mode 100644 index 000000000..99bdc400e --- /dev/null +++ b/Source/JavaScriptCore/wtf/TCPageMap.h @@ -0,0 +1,316 @@ +// Copyright (c) 2005, Google Inc. +// 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 Google Inc. 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. + +// --- +// Author: Sanjay Ghemawat <opensource@google.com> +// +// A data structure used by the caching malloc. It maps from page# to +// a pointer that contains info about that page. We use two +// representations: one for 32-bit addresses, and another for 64 bit +// addresses. Both representations provide the same interface. The +// first representation is implemented as a flat array, the seconds as +// a three-level radix tree that strips away approximately 1/3rd of +// the bits every time. +// +// The BITS parameter should be the number of bits required to hold +// a page number. E.g., with 32 bit pointers and 4K pages (i.e., +// page offset fits in lower 12 bits), BITS == 20. + +#ifndef TCMALLOC_PAGEMAP_H__ +#define TCMALLOC_PAGEMAP_H__ + +#if HAVE(STDINT_H) +#include <stdint.h> +#elif HAVE(INTTYPES_H) +#include <inttypes.h> +#else +#include <sys/types.h> +#endif + +#include <string.h> +#include "Assertions.h" + +// Single-level array +template <int BITS> +class TCMalloc_PageMap1 { + private: + void** array_; + + public: + typedef uintptr_t Number; + + void init(void* (*allocator)(size_t)) { + array_ = reinterpret_cast<void**>((*allocator)(sizeof(void*) << BITS)); + memset(array_, 0, sizeof(void*) << BITS); + } + + // Ensure that the map contains initialized entries "x .. x+n-1". + // Returns true if successful, false if we could not allocate memory. + bool Ensure(Number, size_t) { + // Nothing to do since flat array was allocate at start + return true; + } + + void PreallocateMoreMemory() {} + + // REQUIRES "k" is in range "[0,2^BITS-1]". + // REQUIRES "k" has been ensured before. + // + // Return the current value for KEY. Returns "Value()" if not + // yet set. + void* get(Number k) const { + return array_[k]; + } + + // REQUIRES "k" is in range "[0,2^BITS-1]". + // REQUIRES "k" has been ensured before. + // + // Sets the value for KEY. + void set(Number k, void* v) { + array_[k] = v; + } +}; + +// Two-level radix tree +template <int BITS> +class TCMalloc_PageMap2 { + private: + // Put 32 entries in the root and (2^BITS)/32 entries in each leaf. + static const int ROOT_BITS = 5; + static const int ROOT_LENGTH = 1 << ROOT_BITS; + + static const int LEAF_BITS = BITS - ROOT_BITS; + static const int LEAF_LENGTH = 1 << LEAF_BITS; + + // Leaf node + struct Leaf { + void* values[LEAF_LENGTH]; + }; + + Leaf* root_[ROOT_LENGTH]; // Pointers to 32 child nodes + void* (*allocator_)(size_t); // Memory allocator + + public: + typedef uintptr_t Number; + + void init(void* (*allocator)(size_t)) { + allocator_ = allocator; + memset(root_, 0, sizeof(root_)); + } + + void* get(Number k) const { + ASSERT(k >> BITS == 0); + const Number i1 = k >> LEAF_BITS; + const Number i2 = k & (LEAF_LENGTH-1); + return root_[i1]->values[i2]; + } + + void set(Number k, void* v) { + ASSERT(k >> BITS == 0); + const Number i1 = k >> LEAF_BITS; + const Number i2 = k & (LEAF_LENGTH-1); + root_[i1]->values[i2] = v; + } + + bool Ensure(Number start, size_t n) { + for (Number key = start; key <= start + n - 1; ) { + const Number i1 = key >> LEAF_BITS; + + // Make 2nd level node if necessary + if (root_[i1] == NULL) { + Leaf* leaf = reinterpret_cast<Leaf*>((*allocator_)(sizeof(Leaf))); + if (leaf == NULL) return false; + memset(leaf, 0, sizeof(*leaf)); + root_[i1] = leaf; + } + + // Advance key past whatever is covered by this leaf node + key = ((key >> LEAF_BITS) + 1) << LEAF_BITS; + } + return true; + } + + void PreallocateMoreMemory() { + // Allocate enough to keep track of all possible pages + Ensure(0, 1 << BITS); + } + +#ifdef WTF_CHANGES + template<class Visitor, class MemoryReader> + void visitValues(Visitor& visitor, const MemoryReader& reader) + { + for (int i = 0; i < ROOT_LENGTH; i++) { + if (!root_[i]) + continue; + + Leaf* l = reader(reinterpret_cast<Leaf*>(root_[i])); + for (int j = 0; j < LEAF_LENGTH; j += visitor.visit(l->values[j])) + ; + } + } + + template<class Visitor, class MemoryReader> + void visitAllocations(Visitor& visitor, const MemoryReader&) { + for (int i = 0; i < ROOT_LENGTH; i++) { + if (root_[i]) + visitor.visit(root_[i], sizeof(Leaf)); + } + } +#endif +}; + +// Three-level radix tree +template <int BITS> +class TCMalloc_PageMap3 { + private: + // How many bits should we consume at each interior level + static const int INTERIOR_BITS = (BITS + 2) / 3; // Round-up + static const int INTERIOR_LENGTH = 1 << INTERIOR_BITS; + + // How many bits should we consume at leaf level + static const int LEAF_BITS = BITS - 2*INTERIOR_BITS; + static const int LEAF_LENGTH = 1 << LEAF_BITS; + + // Interior node + struct Node { + Node* ptrs[INTERIOR_LENGTH]; + }; + + // Leaf node + struct Leaf { + void* values[LEAF_LENGTH]; + }; + + Node* root_; // Root of radix tree + void* (*allocator_)(size_t); // Memory allocator + + Node* NewNode() { + Node* result = reinterpret_cast<Node*>((*allocator_)(sizeof(Node))); + if (result != NULL) { + memset(result, 0, sizeof(*result)); + } + return result; + } + + public: + typedef uintptr_t Number; + + void init(void* (*allocator)(size_t)) { + allocator_ = allocator; + root_ = NewNode(); + } + + void* get(Number k) const { + ASSERT(k >> BITS == 0); + const Number i1 = k >> (LEAF_BITS + INTERIOR_BITS); + const Number i2 = (k >> LEAF_BITS) & (INTERIOR_LENGTH-1); + const Number i3 = k & (LEAF_LENGTH-1); + return reinterpret_cast<Leaf*>(root_->ptrs[i1]->ptrs[i2])->values[i3]; + } + + void set(Number k, void* v) { + ASSERT(k >> BITS == 0); + const Number i1 = k >> (LEAF_BITS + INTERIOR_BITS); + const Number i2 = (k >> LEAF_BITS) & (INTERIOR_LENGTH-1); + const Number i3 = k & (LEAF_LENGTH-1); + reinterpret_cast<Leaf*>(root_->ptrs[i1]->ptrs[i2])->values[i3] = v; + } + + bool Ensure(Number start, size_t n) { + for (Number key = start; key <= start + n - 1; ) { + const Number i1 = key >> (LEAF_BITS + INTERIOR_BITS); + const Number i2 = (key >> LEAF_BITS) & (INTERIOR_LENGTH-1); + + // Make 2nd level node if necessary + if (root_->ptrs[i1] == NULL) { + Node* n = NewNode(); + if (n == NULL) return false; + root_->ptrs[i1] = n; + } + + // Make leaf node if necessary + if (root_->ptrs[i1]->ptrs[i2] == NULL) { + Leaf* leaf = reinterpret_cast<Leaf*>((*allocator_)(sizeof(Leaf))); + if (leaf == NULL) return false; + memset(leaf, 0, sizeof(*leaf)); + root_->ptrs[i1]->ptrs[i2] = reinterpret_cast<Node*>(leaf); + } + + // Advance key past whatever is covered by this leaf node + key = ((key >> LEAF_BITS) + 1) << LEAF_BITS; + } + return true; + } + + void PreallocateMoreMemory() { + } + +#ifdef WTF_CHANGES + template<class Visitor, class MemoryReader> + void visitValues(Visitor& visitor, const MemoryReader& reader) { + Node* root = reader(root_); + for (int i = 0; i < INTERIOR_LENGTH; i++) { + if (!root->ptrs[i]) + continue; + + Node* n = reader(root->ptrs[i]); + for (int j = 0; j < INTERIOR_LENGTH; j++) { + if (!n->ptrs[j]) + continue; + + Leaf* l = reader(reinterpret_cast<Leaf*>(n->ptrs[j])); + for (int k = 0; k < LEAF_LENGTH; k += visitor.visit(l->values[k])) + ; + } + } + } + + template<class Visitor, class MemoryReader> + void visitAllocations(Visitor& visitor, const MemoryReader& reader) { + visitor.visit(root_, sizeof(Node)); + + Node* root = reader(root_); + for (int i = 0; i < INTERIOR_LENGTH; i++) { + if (!root->ptrs[i]) + continue; + + visitor.visit(root->ptrs[i], sizeof(Node)); + Node* n = reader(root->ptrs[i]); + for (int j = 0; j < INTERIOR_LENGTH; j++) { + if (!n->ptrs[j]) + continue; + + visitor.visit(n->ptrs[j], sizeof(Leaf)); + } + } + } +#endif +}; + +#endif // TCMALLOC_PAGEMAP_H__ diff --git a/Source/JavaScriptCore/wtf/TCSpinLock.h b/Source/JavaScriptCore/wtf/TCSpinLock.h new file mode 100644 index 000000000..81b7d0cae --- /dev/null +++ b/Source/JavaScriptCore/wtf/TCSpinLock.h @@ -0,0 +1,284 @@ +// Copyright (c) 2005, 2006, Google Inc. +// Copyright (c) 2010, Patrick Gansterer <paroga@paroga.com> +// 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 Google Inc. 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. + +// --- +// Author: Sanjay Ghemawat <opensource@google.com> + +#ifndef TCMALLOC_INTERNAL_SPINLOCK_H__ +#define TCMALLOC_INTERNAL_SPINLOCK_H__ + +#if (CPU(X86) || CPU(X86_64) || CPU(PPC)) && (COMPILER(GCC) || COMPILER(MSVC)) + +#include <time.h> /* For nanosleep() */ + +#if HAVE(STDINT_H) +#include <stdint.h> +#elif HAVE(INTTYPES_H) +#include <inttypes.h> +#else +#include <sys/types.h> +#endif + +#if OS(WINDOWS) +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN +#endif +#include <windows.h> +#else +#include <sched.h> /* For sched_yield() */ +#endif + +static void TCMalloc_SlowLock(volatile unsigned int* lockword); + +// The following is a struct so that it can be initialized at compile time +struct TCMalloc_SpinLock { + + inline void Lock() { + int r; +#if COMPILER(GCC) +#if CPU(X86) || CPU(X86_64) + __asm__ __volatile__ + ("xchgl %0, %1" + : "=r"(r), "=m"(lockword_) + : "0"(1), "m"(lockword_) + : "memory"); +#else + volatile unsigned int *lockword_ptr = &lockword_; + __asm__ __volatile__ + ("1: lwarx %0, 0, %1\n\t" + "stwcx. %2, 0, %1\n\t" + "bne- 1b\n\t" + "isync" + : "=&r" (r), "=r" (lockword_ptr) + : "r" (1), "1" (lockword_ptr) + : "memory"); +#endif +#elif COMPILER(MSVC) + __asm { + mov eax, this ; store &lockword_ (which is this+0) in eax + mov ebx, 1 ; store 1 in ebx + xchg [eax], ebx ; exchange lockword_ and 1 + mov r, ebx ; store old value of lockword_ in r + } +#endif + if (r) TCMalloc_SlowLock(&lockword_); + } + + inline void Unlock() { +#if COMPILER(GCC) +#if CPU(X86) || CPU(X86_64) + __asm__ __volatile__ + ("movl $0, %0" + : "=m"(lockword_) + : "m" (lockword_) + : "memory"); +#else + __asm__ __volatile__ + ("isync\n\t" + "eieio\n\t" + "stw %1, %0" +#if OS(DARWIN) || CPU(PPC) + : "=o" (lockword_) +#else + : "=m" (lockword_) +#endif + : "r" (0) + : "memory"); +#endif +#elif COMPILER(MSVC) + __asm { + mov eax, this ; store &lockword_ (which is this+0) in eax + mov [eax], 0 ; set lockword_ to 0 + } +#endif + } + // Report if we think the lock can be held by this thread. + // When the lock is truly held by the invoking thread + // we will always return true. + // Indended to be used as CHECK(lock.IsHeld()); + inline bool IsHeld() const { + return lockword_ != 0; + } + + inline void Init() { lockword_ = 0; } + + volatile unsigned int lockword_; +}; + +#define SPINLOCK_INITIALIZER { 0 } + +static void TCMalloc_SlowLock(volatile unsigned int* lockword) { +// Yield immediately since fast path failed +#if OS(WINDOWS) + Sleep(0); +#else + sched_yield(); +#endif + while (true) { + int r; +#if COMPILER(GCC) +#if CPU(X86) || CPU(X86_64) + __asm__ __volatile__ + ("xchgl %0, %1" + : "=r"(r), "=m"(*lockword) + : "0"(1), "m"(*lockword) + : "memory"); + +#else + int tmp = 1; + __asm__ __volatile__ + ("1: lwarx %0, 0, %1\n\t" + "stwcx. %2, 0, %1\n\t" + "bne- 1b\n\t" + "isync" + : "=&r" (r), "=r" (lockword) + : "r" (tmp), "1" (lockword) + : "memory"); +#endif +#elif COMPILER(MSVC) + __asm { + mov eax, lockword ; assign lockword into eax + mov ebx, 1 ; assign 1 into ebx + xchg [eax], ebx ; exchange *lockword and 1 + mov r, ebx ; store old value of *lockword in r + } +#endif + if (!r) { + return; + } + + // This code was adapted from the ptmalloc2 implementation of + // spinlocks which would sched_yield() upto 50 times before + // sleeping once for a few milliseconds. Mike Burrows suggested + // just doing one sched_yield() outside the loop and always + // sleeping after that. This change helped a great deal on the + // performance of spinlocks under high contention. A test program + // with 10 threads on a dual Xeon (four virtual processors) went + // from taking 30 seconds to 16 seconds. + + // Sleep for a few milliseconds +#if OS(WINDOWS) + Sleep(2); +#else + struct timespec tm; + tm.tv_sec = 0; + tm.tv_nsec = 2000001; + nanosleep(&tm, NULL); +#endif + } +} + +#elif OS(WINDOWS) + +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN +#endif +#include <windows.h> + +static void TCMalloc_SlowLock(LPLONG lockword); + +// The following is a struct so that it can be initialized at compile time +struct TCMalloc_SpinLock { + + inline void Lock() { + if (InterlockedExchange(&m_lockword, 1)) + TCMalloc_SlowLock(&m_lockword); + } + + inline void Unlock() { + InterlockedExchange(&m_lockword, 0); + } + + inline bool IsHeld() const { + return m_lockword != 0; + } + + inline void Init() { m_lockword = 0; } + + LONG m_lockword; +}; + +#define SPINLOCK_INITIALIZER { 0 } + +static void TCMalloc_SlowLock(LPLONG lockword) { + Sleep(0); // Yield immediately since fast path failed + while (InterlockedExchange(lockword, 1)) + Sleep(2); +} + +#else + +#include <pthread.h> + +// Portable version +struct TCMalloc_SpinLock { + pthread_mutex_t private_lock_; + + inline void Init() { + if (pthread_mutex_init(&private_lock_, NULL) != 0) CRASH(); + } + inline void Finalize() { + if (pthread_mutex_destroy(&private_lock_) != 0) CRASH(); + } + inline void Lock() { + if (pthread_mutex_lock(&private_lock_) != 0) CRASH(); + } + inline void Unlock() { + if (pthread_mutex_unlock(&private_lock_) != 0) CRASH(); + } + bool IsHeld() { + if (pthread_mutex_trylock(&private_lock_)) + return true; + + Unlock(); + return false; + } +}; + +#define SPINLOCK_INITIALIZER { PTHREAD_MUTEX_INITIALIZER } + +#endif + +// Corresponding locker object that arranges to acquire a spinlock for +// the duration of a C++ scope. +class TCMalloc_SpinLockHolder { + private: + TCMalloc_SpinLock* lock_; + public: + inline explicit TCMalloc_SpinLockHolder(TCMalloc_SpinLock* l) + : lock_(l) { l->Lock(); } + inline ~TCMalloc_SpinLockHolder() { lock_->Unlock(); } +}; + +// Short-hands for convenient use by tcmalloc.cc +typedef TCMalloc_SpinLock SpinLock; +typedef TCMalloc_SpinLockHolder SpinLockHolder; + +#endif // TCMALLOC_INTERNAL_SPINLOCK_H__ diff --git a/Source/JavaScriptCore/wtf/TCSystemAlloc.cpp b/Source/JavaScriptCore/wtf/TCSystemAlloc.cpp new file mode 100644 index 000000000..1cd89e631 --- /dev/null +++ b/Source/JavaScriptCore/wtf/TCSystemAlloc.cpp @@ -0,0 +1,530 @@ +// Copyright (c) 2005, 2007, Google Inc. +// 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 Google Inc. 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. + +// --- +// Author: Sanjay Ghemawat + +#include "config.h" +#if !(defined(USE_SYSTEM_MALLOC) && USE_SYSTEM_MALLOC) +#include "TCSystemAlloc.h" + +#include <algorithm> +#include "Assertions.h" +#include "TCSpinLock.h" +#include "UnusedParam.h" +#include "VMTags.h" + +#if HAVE(STDINT_H) +#include <stdint.h> +#elif HAVE(INTTYPES_H) +#include <inttypes.h> +#else +#include <sys/types.h> +#endif + +#if OS(WINDOWS) +#include "windows.h" +#else +#include <errno.h> +#include <unistd.h> +#include <sys/mman.h> +#endif + +#ifndef MAP_ANONYMOUS +#define MAP_ANONYMOUS MAP_ANON +#endif + +using namespace std; + +// Structure for discovering alignment +union MemoryAligner { + void* p; + double d; + size_t s; +}; + +static SpinLock spinlock = SPINLOCK_INITIALIZER; + +// Page size is initialized on demand +static size_t pagesize = 0; + +// Configuration parameters. +// +// if use_devmem is true, either use_sbrk or use_mmap must also be true. +// For 2.2 kernels, it looks like the sbrk address space (500MBish) and +// the mmap address space (1300MBish) are disjoint, so we need both allocators +// to get as much virtual memory as possible. +#ifndef WTF_CHANGES +static bool use_devmem = false; +#endif + +#if HAVE(SBRK) +static bool use_sbrk = false; +#endif + +#if HAVE(MMAP) +static bool use_mmap = true; +#endif + +#if HAVE(VIRTUALALLOC) +static bool use_VirtualAlloc = true; +#endif + +// Flags to keep us from retrying allocators that failed. +static bool devmem_failure = false; +static bool sbrk_failure = false; +static bool mmap_failure = false; +static bool VirtualAlloc_failure = false; + +#ifndef WTF_CHANGES +DEFINE_int32(malloc_devmem_start, 0, + "Physical memory starting location in MB for /dev/mem allocation." + " Setting this to 0 disables /dev/mem allocation"); +DEFINE_int32(malloc_devmem_limit, 0, + "Physical memory limit location in MB for /dev/mem allocation." + " Setting this to 0 means no limit."); +#else +static const int32_t FLAGS_malloc_devmem_start = 0; +static const int32_t FLAGS_malloc_devmem_limit = 0; +#endif + +#if HAVE(SBRK) + +static void* TrySbrk(size_t size, size_t *actual_size, size_t alignment) { + size = ((size + alignment - 1) / alignment) * alignment; + + // could theoretically return the "extra" bytes here, but this + // is simple and correct. + if (actual_size) + *actual_size = size; + + void* result = sbrk(size); + if (result == reinterpret_cast<void*>(-1)) { + sbrk_failure = true; + return NULL; + } + + // Is it aligned? + uintptr_t ptr = reinterpret_cast<uintptr_t>(result); + if ((ptr & (alignment-1)) == 0) return result; + + // Try to get more memory for alignment + size_t extra = alignment - (ptr & (alignment-1)); + void* r2 = sbrk(extra); + if (reinterpret_cast<uintptr_t>(r2) == (ptr + size)) { + // Contiguous with previous result + return reinterpret_cast<void*>(ptr + extra); + } + + // Give up and ask for "size + alignment - 1" bytes so + // that we can find an aligned region within it. + result = sbrk(size + alignment - 1); + if (result == reinterpret_cast<void*>(-1)) { + sbrk_failure = true; + return NULL; + } + ptr = reinterpret_cast<uintptr_t>(result); + if ((ptr & (alignment-1)) != 0) { + ptr += alignment - (ptr & (alignment-1)); + } + return reinterpret_cast<void*>(ptr); +} + +#endif /* HAVE(SBRK) */ + +#if HAVE(MMAP) + +static void* TryMmap(size_t size, size_t *actual_size, size_t alignment) { + // Enforce page alignment + if (pagesize == 0) pagesize = getpagesize(); + if (alignment < pagesize) alignment = pagesize; + size = ((size + alignment - 1) / alignment) * alignment; + + // could theoretically return the "extra" bytes here, but this + // is simple and correct. + if (actual_size) + *actual_size = size; + + // Ask for extra memory if alignment > pagesize + size_t extra = 0; + if (alignment > pagesize) { + extra = alignment - pagesize; + } + void* result = mmap(NULL, size + extra, + PROT_READ | PROT_WRITE, + MAP_PRIVATE|MAP_ANONYMOUS, + VM_TAG_FOR_TCMALLOC_MEMORY, 0); + if (result == reinterpret_cast<void*>(MAP_FAILED)) { + mmap_failure = true; + return NULL; + } + + // Adjust the return memory so it is aligned + uintptr_t ptr = reinterpret_cast<uintptr_t>(result); + size_t adjust = 0; + if ((ptr & (alignment - 1)) != 0) { + adjust = alignment - (ptr & (alignment - 1)); + } + + // Return the unused memory to the system + if (adjust > 0) { + munmap(reinterpret_cast<void*>(ptr), adjust); + } + if (adjust < extra) { + munmap(reinterpret_cast<void*>(ptr + adjust + size), extra - adjust); + } + + ptr += adjust; + return reinterpret_cast<void*>(ptr); +} + +#endif /* HAVE(MMAP) */ + +#if HAVE(VIRTUALALLOC) + +static void* TryVirtualAlloc(size_t size, size_t *actual_size, size_t alignment) { + // Enforce page alignment + if (pagesize == 0) { + SYSTEM_INFO system_info; + GetSystemInfo(&system_info); + pagesize = system_info.dwPageSize; + } + + if (alignment < pagesize) alignment = pagesize; + size = ((size + alignment - 1) / alignment) * alignment; + + // could theoretically return the "extra" bytes here, but this + // is simple and correct. + if (actual_size) + *actual_size = size; + + // Ask for extra memory if alignment > pagesize + size_t extra = 0; + if (alignment > pagesize) { + extra = alignment - pagesize; + } + void* result = VirtualAlloc(NULL, size + extra, + MEM_RESERVE | MEM_COMMIT | MEM_TOP_DOWN, + PAGE_READWRITE); + + if (result == NULL) { + VirtualAlloc_failure = true; + return NULL; + } + + // Adjust the return memory so it is aligned + uintptr_t ptr = reinterpret_cast<uintptr_t>(result); + size_t adjust = 0; + if ((ptr & (alignment - 1)) != 0) { + adjust = alignment - (ptr & (alignment - 1)); + } + + // Return the unused memory to the system - we'd like to release but the best we can do + // is decommit, since Windows only lets you free the whole allocation. + if (adjust > 0) { + VirtualFree(reinterpret_cast<void*>(ptr), adjust, MEM_DECOMMIT); + } + if (adjust < extra) { + VirtualFree(reinterpret_cast<void*>(ptr + adjust + size), extra-adjust, MEM_DECOMMIT); + } + + ptr += adjust; + return reinterpret_cast<void*>(ptr); +} + +#endif /* HAVE(MMAP) */ + +#ifndef WTF_CHANGES +static void* TryDevMem(size_t size, size_t *actual_size, size_t alignment) { + static bool initialized = false; + static off_t physmem_base; // next physical memory address to allocate + static off_t physmem_limit; // maximum physical address allowed + static int physmem_fd; // file descriptor for /dev/mem + + // Check if we should use /dev/mem allocation. Note that it may take + // a while to get this flag initialized, so meanwhile we fall back to + // the next allocator. (It looks like 7MB gets allocated before + // this flag gets initialized -khr.) + if (FLAGS_malloc_devmem_start == 0) { + // NOTE: not a devmem_failure - we'd like TCMalloc_SystemAlloc to + // try us again next time. + return NULL; + } + + if (!initialized) { + physmem_fd = open("/dev/mem", O_RDWR); + if (physmem_fd < 0) { + devmem_failure = true; + return NULL; + } + physmem_base = FLAGS_malloc_devmem_start*1024LL*1024LL; + physmem_limit = FLAGS_malloc_devmem_limit*1024LL*1024LL; + initialized = true; + } + + // Enforce page alignment + if (pagesize == 0) pagesize = getpagesize(); + if (alignment < pagesize) alignment = pagesize; + size = ((size + alignment - 1) / alignment) * alignment; + + // could theoretically return the "extra" bytes here, but this + // is simple and correct. + if (actual_size) + *actual_size = size; + + // Ask for extra memory if alignment > pagesize + size_t extra = 0; + if (alignment > pagesize) { + extra = alignment - pagesize; + } + + // check to see if we have any memory left + if (physmem_limit != 0 && physmem_base + size + extra > physmem_limit) { + devmem_failure = true; + return NULL; + } + void *result = mmap(0, size + extra, PROT_READ | PROT_WRITE, + MAP_SHARED, physmem_fd, physmem_base); + if (result == reinterpret_cast<void*>(MAP_FAILED)) { + devmem_failure = true; + return NULL; + } + uintptr_t ptr = reinterpret_cast<uintptr_t>(result); + + // Adjust the return memory so it is aligned + size_t adjust = 0; + if ((ptr & (alignment - 1)) != 0) { + adjust = alignment - (ptr & (alignment - 1)); + } + + // Return the unused virtual memory to the system + if (adjust > 0) { + munmap(reinterpret_cast<void*>(ptr), adjust); + } + if (adjust < extra) { + munmap(reinterpret_cast<void*>(ptr + adjust + size), extra - adjust); + } + + ptr += adjust; + physmem_base += adjust + size; + + return reinterpret_cast<void*>(ptr); +} +#endif + +void* TCMalloc_SystemAlloc(size_t size, size_t *actual_size, size_t alignment) { + // Discard requests that overflow + if (size + alignment < size) return NULL; + + SpinLockHolder lock_holder(&spinlock); + + // Enforce minimum alignment + if (alignment < sizeof(MemoryAligner)) alignment = sizeof(MemoryAligner); + + // Try twice, once avoiding allocators that failed before, and once + // more trying all allocators even if they failed before. + for (int i = 0; i < 2; i++) { + +#ifndef WTF_CHANGES + if (use_devmem && !devmem_failure) { + void* result = TryDevMem(size, actual_size, alignment); + if (result != NULL) return result; + } +#endif + +#if HAVE(SBRK) + if (use_sbrk && !sbrk_failure) { + void* result = TrySbrk(size, actual_size, alignment); + if (result != NULL) return result; + } +#endif + +#if HAVE(MMAP) + if (use_mmap && !mmap_failure) { + void* result = TryMmap(size, actual_size, alignment); + if (result != NULL) return result; + } +#endif + +#if HAVE(VIRTUALALLOC) + if (use_VirtualAlloc && !VirtualAlloc_failure) { + void* result = TryVirtualAlloc(size, actual_size, alignment); + if (result != NULL) return result; + } +#endif + + // nothing worked - reset failure flags and try again + devmem_failure = false; + sbrk_failure = false; + mmap_failure = false; + VirtualAlloc_failure = false; + } + return NULL; +} + +#if HAVE(MADV_FREE_REUSE) + +void TCMalloc_SystemRelease(void* start, size_t length) +{ + int madviseResult; + + while ((madviseResult = madvise(start, length, MADV_FREE_REUSABLE)) == -1 && errno == EAGAIN) { } + + // Although really advisory, if madvise fail, we want to know about it. + ASSERT_UNUSED(madviseResult, madviseResult != -1); +} + +#elif HAVE(MADV_FREE) || HAVE(MADV_DONTNEED) + +void TCMalloc_SystemRelease(void* start, size_t length) +{ + // MADV_FREE clears the modified bit on pages, which allows + // them to be discarded immediately. +#if HAVE(MADV_FREE) + const int advice = MADV_FREE; +#else + const int advice = MADV_DONTNEED; +#endif + if (FLAGS_malloc_devmem_start) { + // It's not safe to use MADV_DONTNEED if we've been mapping + // /dev/mem for heap memory + return; + } + if (pagesize == 0) pagesize = getpagesize(); + const size_t pagemask = pagesize - 1; + + size_t new_start = reinterpret_cast<size_t>(start); + size_t end = new_start + length; + size_t new_end = end; + + // Round up the starting address and round down the ending address + // to be page aligned: + new_start = (new_start + pagesize - 1) & ~pagemask; + new_end = new_end & ~pagemask; + + ASSERT((new_start & pagemask) == 0); + ASSERT((new_end & pagemask) == 0); + ASSERT(new_start >= reinterpret_cast<size_t>(start)); + ASSERT(new_end <= end); + + if (new_end > new_start) { + // Note -- ignoring most return codes, because if this fails it + // doesn't matter... + while (madvise(reinterpret_cast<char*>(new_start), new_end - new_start, + advice) == -1 && + errno == EAGAIN) { + // NOP + } + } +} + +#elif HAVE(MMAP) + +void TCMalloc_SystemRelease(void* start, size_t length) +{ + void* newAddress = mmap(start, length, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0); + // If the mmap failed then that's ok, we just won't return the memory to the system. + ASSERT_UNUSED(newAddress, newAddress == start || newAddress == reinterpret_cast<void*>(MAP_FAILED)); +} + +#elif HAVE(VIRTUALALLOC) + +void TCMalloc_SystemRelease(void* start, size_t length) +{ + if (VirtualFree(start, length, MEM_DECOMMIT)) + return; + + // The decommit may fail if the memory region consists of allocations + // from more than one call to VirtualAlloc. In this case, fall back to + // using VirtualQuery to retrieve the allocation boundaries and decommit + // them each individually. + + char* ptr = static_cast<char*>(start); + char* end = ptr + length; + MEMORY_BASIC_INFORMATION info; + while (ptr < end) { + size_t resultSize = VirtualQuery(ptr, &info, sizeof(info)); + ASSERT_UNUSED(resultSize, resultSize == sizeof(info)); + + size_t decommitSize = min<size_t>(info.RegionSize, end - ptr); + BOOL success = VirtualFree(ptr, decommitSize, MEM_DECOMMIT); + ASSERT_UNUSED(success, success); + ptr += decommitSize; + } +} + +#else + +// Platforms that don't support returning memory use an empty inline version of TCMalloc_SystemRelease +// declared in TCSystemAlloc.h + +#endif + +#if HAVE(MADV_FREE_REUSE) + +void TCMalloc_SystemCommit(void* start, size_t length) +{ + while (madvise(start, length, MADV_FREE_REUSE) == -1 && errno == EAGAIN) { } +} + +#elif HAVE(VIRTUALALLOC) + +void TCMalloc_SystemCommit(void* start, size_t length) +{ + if (VirtualAlloc(start, length, MEM_COMMIT, PAGE_READWRITE) == start) + return; + + // The commit may fail if the memory region consists of allocations + // from more than one call to VirtualAlloc. In this case, fall back to + // using VirtualQuery to retrieve the allocation boundaries and commit them + // each individually. + + char* ptr = static_cast<char*>(start); + char* end = ptr + length; + MEMORY_BASIC_INFORMATION info; + while (ptr < end) { + size_t resultSize = VirtualQuery(ptr, &info, sizeof(info)); + ASSERT_UNUSED(resultSize, resultSize == sizeof(info)); + + size_t commitSize = min<size_t>(info.RegionSize, end - ptr); + void* newAddress = VirtualAlloc(ptr, commitSize, MEM_COMMIT, PAGE_READWRITE); + ASSERT_UNUSED(newAddress, newAddress == ptr); + ptr += commitSize; + } +} + +#else + +// Platforms that don't need to explicitly commit memory use an empty inline version of TCMalloc_SystemCommit +// declared in TCSystemAlloc.h + +#endif + +#endif // #if !(defined(USE_SYSTEM_MALLOC) && USE_SYSTEM_MALLOC) + diff --git a/Source/JavaScriptCore/wtf/TCSystemAlloc.h b/Source/JavaScriptCore/wtf/TCSystemAlloc.h new file mode 100644 index 000000000..1c677889c --- /dev/null +++ b/Source/JavaScriptCore/wtf/TCSystemAlloc.h @@ -0,0 +1,75 @@ +// Copyright (c) 2005, 2007, Google Inc. +// 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 Google Inc. 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. + +// --- +// Author: Sanjay Ghemawat +// +// Routine that uses sbrk/mmap to allocate memory from the system. +// Useful for implementing malloc. + +#ifndef TCMALLOC_SYSTEM_ALLOC_H__ +#define TCMALLOC_SYSTEM_ALLOC_H__ + +// REQUIRES: "alignment" is a power of two or "0" to indicate default alignment +// +// Allocate and return "N" bytes of zeroed memory. +// +// If actual_bytes is NULL then the returned memory is exactly the +// requested size. If actual bytes is non-NULL then the allocator +// may optionally return more bytes than asked for (i.e. return an +// entire "huge" page if a huge page allocator is in use). +// +// The returned pointer is a multiple of "alignment" if non-zero. +// +// Returns NULL when out of memory. +extern void* TCMalloc_SystemAlloc(size_t bytes, size_t *actual_bytes, + size_t alignment = 0); + +// This call is a hint to the operating system that the pages +// contained in the specified range of memory will not be used for a +// while, and can be released for use by other processes or the OS. +// Pages which are released in this way may be destroyed (zeroed) by +// the OS. The benefit of this function is that it frees memory for +// use by the system, the cost is that the pages are faulted back into +// the address space next time they are touched, which can impact +// performance. (Only pages fully covered by the memory region will +// be released, partial pages will not.) +extern void TCMalloc_SystemRelease(void* start, size_t length); + +extern void TCMalloc_SystemCommit(void* start, size_t length); + +#if !HAVE(MADV_FREE_REUSE) && !HAVE(MADV_DONTNEED) && !HAVE(MMAP) && !HAVE(VIRTUALALLOC) +inline void TCMalloc_SystemRelease(void*, size_t) { } +#endif + +#if !HAVE(VIRTUALALLOC) && !HAVE(MADV_FREE_REUSE) +inline void TCMalloc_SystemCommit(void*, size_t) { } +#endif + +#endif /* TCMALLOC_SYSTEM_ALLOC_H__ */ diff --git a/Source/JavaScriptCore/wtf/TemporaryChange.h b/Source/JavaScriptCore/wtf/TemporaryChange.h new file mode 100644 index 000000000..fe374b194 --- /dev/null +++ b/Source/JavaScriptCore/wtf/TemporaryChange.h @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2011 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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 APPLE AND ITS 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 APPLE OR ITS 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. + */ + +#ifndef TemporaryChange_h +#define TemporaryChange_h + +#include "Noncopyable.h" + +namespace WTF { + +// TemporaryChange<> is useful for setting a variable to a new value only within a +// particular scope. An TemporaryChange<> object changes a variable to its original +// value upon destruction, making it an alternative to writing "var = false;" +// or "var = oldVal;" at all of a block's exit points. +// +// This should be obvious, but note that an TemporaryChange<> instance should have a +// shorter lifetime than its scopedVariable, to prevent invalid memory writes +// when the TemporaryChange<> object is destroyed. + +template<typename T> +class TemporaryChange { + WTF_MAKE_NONCOPYABLE(TemporaryChange); +public: + TemporaryChange(T& scopedVariable, T newValue) + : m_scopedVariable(scopedVariable) + , m_originalValue(scopedVariable) + { + m_scopedVariable = newValue; + } + + ~TemporaryChange() + { + m_scopedVariable = m_originalValue; + } + + +private: + T& m_scopedVariable; + T m_originalValue; +}; + +} + +using WTF::TemporaryChange; + +#endif diff --git a/Source/JavaScriptCore/wtf/ThreadFunctionInvocation.h b/Source/JavaScriptCore/wtf/ThreadFunctionInvocation.h new file mode 100644 index 000000000..f1e147268 --- /dev/null +++ b/Source/JavaScriptCore/wtf/ThreadFunctionInvocation.h @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2007, 2008 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of Apple Computer, Inc. ("Apple") 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 APPLE AND ITS 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 APPLE OR ITS 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. + */ + +#ifndef ThreadFunctionInvocation_h +#define ThreadFunctionInvocation_h + +namespace WTF { + +typedef void* (*ThreadFunction)(void* argument); + +struct ThreadFunctionInvocation { + ThreadFunctionInvocation(ThreadFunction function, void* data) + : function(function) + , data(data) + { + } + + ThreadFunction function; + void* data; +}; + +} // namespace WTF + +#endif // ThreadFunctionInvocation_h diff --git a/Source/JavaScriptCore/wtf/ThreadIdentifierDataPthreads.cpp b/Source/JavaScriptCore/wtf/ThreadIdentifierDataPthreads.cpp new file mode 100644 index 000000000..b3b690f70 --- /dev/null +++ b/Source/JavaScriptCore/wtf/ThreadIdentifierDataPthreads.cpp @@ -0,0 +1,94 @@ +/* + * Copyright (C) 2009, 2011 Google Inc. 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 Google Inc. 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 "config.h" + +#if USE(PTHREADS) + +#include "ThreadIdentifierDataPthreads.h" + +#include "Threading.h" + +#if OS(ANDROID) +// PTHREAD_KEYS_MAX is not defined in bionic, so explicitly define it here. +#define PTHREAD_KEYS_MAX 1024 +#else +#include <limits.h> +#endif + +namespace WTF { + +pthread_key_t ThreadIdentifierData::m_key = PTHREAD_KEYS_MAX; + +void clearPthreadHandleForIdentifier(ThreadIdentifier); + +ThreadIdentifierData::~ThreadIdentifierData() +{ + clearPthreadHandleForIdentifier(m_identifier); +} + +void ThreadIdentifierData::initializeOnce() +{ + if (pthread_key_create(&m_key, destruct)) + CRASH(); +} + +ThreadIdentifier ThreadIdentifierData::identifier() +{ + ASSERT(m_key != PTHREAD_KEYS_MAX); + ThreadIdentifierData* threadIdentifierData = static_cast<ThreadIdentifierData*>(pthread_getspecific(m_key)); + + return threadIdentifierData ? threadIdentifierData->m_identifier : 0; +} + +void ThreadIdentifierData::initialize(ThreadIdentifier id) +{ + ASSERT(!identifier()); + pthread_setspecific(m_key, new ThreadIdentifierData(id)); +} + +void ThreadIdentifierData::destruct(void* data) +{ + ThreadIdentifierData* threadIdentifierData = static_cast<ThreadIdentifierData*>(data); + ASSERT(threadIdentifierData); + + if (threadIdentifierData->m_isDestroyedOnce) { + delete threadIdentifierData; + return; + } + + threadIdentifierData->m_isDestroyedOnce = true; + // Re-setting the value for key causes another destruct() call after all other thread-specific destructors were called. + pthread_setspecific(m_key, threadIdentifierData); +} + +} // namespace WTF + +#endif // USE(PTHREADS) diff --git a/Source/JavaScriptCore/wtf/ThreadIdentifierDataPthreads.h b/Source/JavaScriptCore/wtf/ThreadIdentifierDataPthreads.h new file mode 100644 index 000000000..84349a0cd --- /dev/null +++ b/Source/JavaScriptCore/wtf/ThreadIdentifierDataPthreads.h @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2009 Google Inc. 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 Google Inc. 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. + */ + +#ifndef ThreadIdentifierDataPthreads_h +#define ThreadIdentifierDataPthreads_h + +#include <wtf/Threading.h> + +namespace WTF { + +// Holds ThreadIdentifier in the thread-specific storage and employs pthreads-specific 2-pass destruction to reliably remove +// ThreadIdentifier from threadMap. It assumes regular ThreadSpecific types don't use multiple-pass destruction. +class ThreadIdentifierData { + WTF_MAKE_NONCOPYABLE(ThreadIdentifierData); +public: + ~ThreadIdentifierData(); + + // One time initialization for this class as a whole. + // This method must be called before initialize() and it is not thread-safe. + static void initializeOnce(); + + // Creates and puts an instance of ThreadIdentifierData into thread-specific storage. + static void initialize(ThreadIdentifier identifier); + + // Returns 0 if thread-specific storage was not initialized. + static ThreadIdentifier identifier(); + +private: + ThreadIdentifierData(ThreadIdentifier identifier) + : m_identifier(identifier) + , m_isDestroyedOnce(false) + { + } + + // This thread-specific destructor is called 2 times when thread terminates: + // - first, when all the other thread-specific destructors are called, it simply remembers it was 'destroyed once' + // and re-sets itself into the thread-specific slot to make Pthreads to call it again later. + // - second, after all thread-specific destructors were invoked, it gets called again - this time, we remove the + // ThreadIdentifier from the threadMap, completing the cleanup. + static void destruct(void* data); + + ThreadIdentifier m_identifier; + bool m_isDestroyedOnce; + static pthread_key_t m_key; +}; + +} // namespace WTF + +#endif // ThreadIdentifierDataPthreads_h + + diff --git a/Source/JavaScriptCore/wtf/ThreadRestrictionVerifier.h b/Source/JavaScriptCore/wtf/ThreadRestrictionVerifier.h new file mode 100644 index 000000000..0eeac8e62 --- /dev/null +++ b/Source/JavaScriptCore/wtf/ThreadRestrictionVerifier.h @@ -0,0 +1,178 @@ +/* + * Copyright (C) 2011 Google Inc. 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 Google Inc. 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. + */ + +#ifndef ThreadRestrictionVerifier_h +#define ThreadRestrictionVerifier_h + +#include <wtf/Assertions.h> +#include <wtf/Threading.h> +#include <wtf/ThreadingPrimitives.h> + +#if HAVE(DISPATCH_H) +#include <dispatch/dispatch.h> +#endif + +#ifndef NDEBUG + +namespace WTF { + +// Verifies that a class is used in a way that respects its lack of thread-safety. +// The default mode is to verify that the object will only be used on a single thread. The +// thread gets captured when setShared(true) is called. +// The mode may be changed by calling useMutexMode (or turnOffVerification). +class ThreadRestrictionVerifier { +public: + ThreadRestrictionVerifier() + : m_mode(SingleThreadVerificationMode) + , m_shared(false) + , m_owningThread(0) + , m_mutex(0) +#if HAVE(DISPATCH_H) + , m_owningQueue(0) +#endif + { + } + +#if HAVE(DISPATCH_H) + ~ThreadRestrictionVerifier() + { + if (m_owningQueue) + dispatch_release(m_owningQueue); + } +#endif + + void setMutexMode(Mutex& mutex) + { + ASSERT(m_mode == SingleThreadVerificationMode || (m_mode == MutexVerificationMode && &mutex == m_mutex)); + m_mode = MutexVerificationMode; + m_mutex = &mutex; + } + +#if HAVE(DISPATCH_H) + void setDispatchQueueMode(dispatch_queue_t queue) + { + ASSERT(m_mode == SingleThreadVerificationMode); + m_mode = SingleDispatchQueueVerificationMode; + m_owningQueue = queue; + dispatch_retain(m_owningQueue); + } +#endif + + void turnOffVerification() + { + ASSERT(m_mode == SingleThreadVerificationMode); + m_mode = NoVerificationMode; + } + + // Indicates that the object may (or may not) be owned by more than one place. + void setShared(bool shared) + { +#if !ASSERT_DISABLED + bool previouslyShared = m_shared; +#endif + m_shared = shared; + + if (!m_shared) + return; + + switch (m_mode) { + case SingleThreadVerificationMode: + ASSERT(shared != previouslyShared); + // Capture the current thread to verify that subsequent ref/deref happen on this thread. + m_owningThread = currentThread(); + return; + +#if HAVE(DISPATCH_H) + case SingleDispatchQueueVerificationMode: +#endif + case MutexVerificationMode: + case NoVerificationMode: + return; + } + ASSERT_NOT_REACHED(); + } + + // Is it OK to use the object at this moment on the current thread? + bool isSafeToUse() const + { + if (!m_shared) + return true; + + switch (m_mode) { + case SingleThreadVerificationMode: + return m_owningThread == currentThread(); + + case MutexVerificationMode: + if (!m_mutex->tryLock()) + return true; + m_mutex->unlock(); + return false; + +#if HAVE(DISPATCH_H) + case SingleDispatchQueueVerificationMode: + return m_owningQueue == dispatch_get_current_queue(); +#endif + + case NoVerificationMode: + return true; + } + ASSERT_NOT_REACHED(); + return true; + } + +private: + enum VerificationMode { + SingleThreadVerificationMode, + MutexVerificationMode, + NoVerificationMode, +#if HAVE(DISPATCH_H) + SingleDispatchQueueVerificationMode, +#endif + }; + + VerificationMode m_mode; + bool m_shared; + + // Used by SingleThreadVerificationMode + ThreadIdentifier m_owningThread; + + // Used by MutexVerificationMode. + Mutex* m_mutex; + +#if HAVE(DISPATCH_H) + // Used by SingleDispatchQueueVerificationMode. + dispatch_queue_t m_owningQueue; +#endif +}; + +} + +#endif +#endif diff --git a/Source/JavaScriptCore/wtf/ThreadSafeRefCounted.h b/Source/JavaScriptCore/wtf/ThreadSafeRefCounted.h new file mode 100644 index 000000000..8497b3bc2 --- /dev/null +++ b/Source/JavaScriptCore/wtf/ThreadSafeRefCounted.h @@ -0,0 +1,150 @@ +/* + * Copyright (C) 2007, 2008, 2010 Apple Inc. All rights reserved. + * Copyright (C) 2007 Justin Haygood (jhaygood@reaktix.com) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of Apple Computer, Inc. ("Apple") 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 APPLE AND ITS 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 APPLE OR ITS 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. + * + * + * Note: The implementations of InterlockedIncrement and InterlockedDecrement are based + * on atomic_increment and atomic_exchange_and_add from the Boost C++ Library. The license + * is virtually identical to the Apple license above but is included here for completeness. + * + * Boost Software License - Version 1.0 - August 17th, 2003 + * + * Permission is hereby granted, free of charge, to any person or organization + * obtaining a copy of the software and accompanying documentation covered by + * this license (the "Software") to use, reproduce, display, distribute, + * execute, and transmit the Software, and to prepare derivative works of the + * Software, and to permit third-parties to whom the Software is furnished to + * do so, all subject to the following: + * + * The copyright notices in the Software and this entire statement, including + * the above license grant, this restriction and the following disclaimer, + * must be included in all copies of the Software, in whole or in part, and + * all derivative works of the Software, unless such copies or derivative + * works are solely in the form of machine-executable object code generated by + * a source language processor. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT + * SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE + * FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#ifndef ThreadSafeRefCounted_h +#define ThreadSafeRefCounted_h + +#include "Platform.h" + +#include <wtf/Atomics.h> +#include <wtf/DynamicAnnotations.h> +#include <wtf/ThreadingPrimitives.h> + +namespace WTF { + +class ThreadSafeRefCountedBase { + WTF_MAKE_NONCOPYABLE(ThreadSafeRefCountedBase); + WTF_MAKE_FAST_ALLOCATED; +public: + ThreadSafeRefCountedBase(int initialRefCount = 1) + : m_refCount(initialRefCount) + { + } + + void ref() + { +#if USE(LOCKFREE_THREADSAFEREFCOUNTED) + atomicIncrement(&m_refCount); +#else + MutexLocker locker(m_mutex); + ++m_refCount; +#endif + } + + bool hasOneRef() + { + return refCount() == 1; + } + + int refCount() const + { +#if !USE(LOCKFREE_THREADSAFEREFCOUNTED) + MutexLocker locker(m_mutex); +#endif + return static_cast<int const volatile &>(m_refCount); + } + +protected: + // Returns whether the pointer should be freed or not. + bool derefBase() + { +#if USE(LOCKFREE_THREADSAFEREFCOUNTED) + WTF_ANNOTATE_HAPPENS_BEFORE(&m_refCount); + if (atomicDecrement(&m_refCount) <= 0) { + WTF_ANNOTATE_HAPPENS_AFTER(&m_refCount); + return true; + } +#else + int refCount; + { + MutexLocker locker(m_mutex); + --m_refCount; + refCount = m_refCount; + } + if (refCount <= 0) + return true; +#endif + return false; + } + +private: + int m_refCount; +#if !USE(LOCKFREE_THREADSAFEREFCOUNTED) + mutable Mutex m_mutex; +#endif +}; + +template<class T> class ThreadSafeRefCounted : public ThreadSafeRefCountedBase { +public: + void deref() + { + if (derefBase()) + delete static_cast<T*>(this); + } + +protected: + ThreadSafeRefCounted() + { + } +}; + +} // namespace WTF + +using WTF::ThreadSafeRefCounted; + +#endif // ThreadSafeRefCounted_h diff --git a/Source/JavaScriptCore/wtf/ThreadSpecific.h b/Source/JavaScriptCore/wtf/ThreadSpecific.h new file mode 100644 index 000000000..7c75a83ee --- /dev/null +++ b/Source/JavaScriptCore/wtf/ThreadSpecific.h @@ -0,0 +1,276 @@ +/* + * Copyright (C) 2008 Apple Inc. All rights reserved. + * Copyright (C) 2009 Jian Li <jianli@chromium.org> + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of Apple Computer, Inc. ("Apple") 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 APPLE AND ITS 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 APPLE OR ITS 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. + */ + +/* Thread local storage is implemented by using either pthread API or Windows + * native API. There is subtle semantic discrepancy for the cleanup function + * implementation as noted below: + * @ In pthread implementation, the destructor function will be called + * repeatedly if there is still non-NULL value associated with the function. + * @ In Windows native implementation, the destructor function will be called + * only once. + * This semantic discrepancy does not impose any problem because nowhere in + * WebKit the repeated call bahavior is utilized. + */ + +#ifndef WTF_ThreadSpecific_h +#define WTF_ThreadSpecific_h + +#include <wtf/Noncopyable.h> +#include <wtf/StdLibExtras.h> + +#if USE(PTHREADS) +#include <pthread.h> +#elif PLATFORM(GTK) +#include <glib.h> +#elif OS(WINDOWS) +#include <windows.h> +#endif + +namespace WTF { + +#if OS(WINDOWS) +// ThreadSpecificThreadExit should be called each time when a thread is detached. +// This is done automatically for threads created with WTF::createThread. +void ThreadSpecificThreadExit(); +#endif + +template<typename T> class ThreadSpecific { + WTF_MAKE_NONCOPYABLE(ThreadSpecific); +public: + ThreadSpecific(); + bool isSet(); // Useful as a fast check to see if this thread has set this value. + T* operator->(); + operator T*(); + T& operator*(); + +private: +#if OS(WINDOWS) + friend void ThreadSpecificThreadExit(); +#endif + + // Not implemented. It's technically possible to destroy a thread specific key, but one would need + // to make sure that all values have been destroyed already (usually, that all threads that used it + // have exited). It's unlikely that any user of this call will be in that situation - and having + // a destructor defined can be confusing, given that it has such strong pre-requisites to work correctly. + ~ThreadSpecific(); + + T* get(); + void set(T*); + void static destroy(void* ptr); + +#if USE(PTHREADS) || PLATFORM(QT) || PLATFORM(GTK) || OS(WINDOWS) + struct Data { + WTF_MAKE_NONCOPYABLE(Data); + public: + Data(T* value, ThreadSpecific<T>* owner) : value(value), owner(owner) {} + + T* value; + ThreadSpecific<T>* owner; +#if OS(WINDOWS) + void (*destructor)(void*); +#endif + }; +#endif + +#if USE(PTHREADS) + pthread_key_t m_key; +#elif PLATFORM(GTK) + GStaticPrivate m_key; +#elif OS(WINDOWS) + int m_index; +#endif +}; + +#if USE(PTHREADS) +template<typename T> +inline ThreadSpecific<T>::ThreadSpecific() +{ + int error = pthread_key_create(&m_key, destroy); + if (error) + CRASH(); +} + +template<typename T> +inline T* ThreadSpecific<T>::get() +{ + Data* data = static_cast<Data*>(pthread_getspecific(m_key)); + return data ? data->value : 0; +} + +template<typename T> +inline void ThreadSpecific<T>::set(T* ptr) +{ + ASSERT(!get()); + pthread_setspecific(m_key, new Data(ptr, this)); +} + +#elif PLATFORM(GTK) + +template<typename T> +inline ThreadSpecific<T>::ThreadSpecific() +{ + g_static_private_init(&m_key); +} + +template<typename T> +inline T* ThreadSpecific<T>::get() +{ + Data* data = static_cast<Data*>(g_static_private_get(&m_key)); + return data ? data->value : 0; +} + +template<typename T> +inline void ThreadSpecific<T>::set(T* ptr) +{ + ASSERT(!get()); + Data* data = new Data(ptr, this); + g_static_private_set(&m_key, data, destroy); +} + +#elif OS(WINDOWS) + +// TLS_OUT_OF_INDEXES is not defined on WinCE. +#ifndef TLS_OUT_OF_INDEXES +#define TLS_OUT_OF_INDEXES 0xffffffff +#endif + +// The maximum number of TLS keys that can be created. For simplification, we assume that: +// 1) Once the instance of ThreadSpecific<> is created, it will not be destructed until the program dies. +// 2) We do not need to hold many instances of ThreadSpecific<> data. This fixed number should be far enough. +const int kMaxTlsKeySize = 256; + +long& tlsKeyCount(); +DWORD* tlsKeys(); + +template<typename T> +inline ThreadSpecific<T>::ThreadSpecific() + : m_index(-1) +{ + DWORD tlsKey = TlsAlloc(); + if (tlsKey == TLS_OUT_OF_INDEXES) + CRASH(); + + m_index = InterlockedIncrement(&tlsKeyCount()) - 1; + if (m_index >= kMaxTlsKeySize) + CRASH(); + tlsKeys()[m_index] = tlsKey; +} + +template<typename T> +inline ThreadSpecific<T>::~ThreadSpecific() +{ + // Does not invoke destructor functions. They will be called from ThreadSpecificThreadExit when the thread is detached. + TlsFree(tlsKeys()[m_index]); +} + +template<typename T> +inline T* ThreadSpecific<T>::get() +{ + Data* data = static_cast<Data*>(TlsGetValue(tlsKeys()[m_index])); + return data ? data->value : 0; +} + +template<typename T> +inline void ThreadSpecific<T>::set(T* ptr) +{ + ASSERT(!get()); + Data* data = new Data(ptr, this); + data->destructor = &ThreadSpecific<T>::destroy; + TlsSetValue(tlsKeys()[m_index], data); +} + +#else +#error ThreadSpecific is not implemented for this platform. +#endif + +template<typename T> +inline void ThreadSpecific<T>::destroy(void* ptr) +{ + Data* data = static_cast<Data*>(ptr); + +#if USE(PTHREADS) + // We want get() to keep working while data destructor works, because it can be called indirectly by the destructor. + // Some pthreads implementations zero out the pointer before calling destroy(), so we temporarily reset it. + pthread_setspecific(data->owner->m_key, ptr); +#elif PLATFORM(GTK) + // See comment as above + g_static_private_set(&data->owner->m_key, data, 0); +#endif + + data->value->~T(); + fastFree(data->value); + +#if USE(PTHREADS) + pthread_setspecific(data->owner->m_key, 0); +#elif PLATFORM(GTK) + g_static_private_set(&data->owner->m_key, 0, 0); +#elif OS(WINDOWS) + TlsSetValue(tlsKeys()[data->owner->m_index], 0); +#else +#error ThreadSpecific is not implemented for this platform. +#endif + + delete data; +} + +template<typename T> +inline bool ThreadSpecific<T>::isSet() +{ + return !!get(); +} + +template<typename T> +inline ThreadSpecific<T>::operator T*() +{ + T* ptr = static_cast<T*>(get()); + if (!ptr) { + // Set up thread-specific value's memory pointer before invoking constructor, in case any function it calls + // needs to access the value, to avoid recursion. + ptr = static_cast<T*>(fastZeroedMalloc(sizeof(T))); + set(ptr); + new (NotNull, ptr) T; + } + return ptr; +} + +template<typename T> +inline T* ThreadSpecific<T>::operator->() +{ + return operator T*(); +} + +template<typename T> +inline T& ThreadSpecific<T>::operator*() +{ + return *operator T*(); +} + +} + +#endif diff --git a/Source/JavaScriptCore/wtf/ThreadSpecificWin.cpp b/Source/JavaScriptCore/wtf/ThreadSpecificWin.cpp new file mode 100644 index 000000000..d72996a7a --- /dev/null +++ b/Source/JavaScriptCore/wtf/ThreadSpecificWin.cpp @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2009 Jian Li <jianli@chromium.org> + * + * This library 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 library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#include "config.h" + +#include "ThreadSpecific.h" + +#if USE(PTHREADS) +#error This file should not be compiled by ports that do not use Windows native ThreadSpecific implementation. +#endif + +namespace WTF { + +long& tlsKeyCount() +{ + static long count; + return count; +} + +DWORD* tlsKeys() +{ + static DWORD keys[kMaxTlsKeySize]; + return keys; +} + +void ThreadSpecificThreadExit() +{ + for (long i = 0; i < tlsKeyCount(); i++) { + // The layout of ThreadSpecific<T>::Data does not depend on T. So we are safe to do the static cast to ThreadSpecific<int> in order to access its data member. + ThreadSpecific<int>::Data* data = static_cast<ThreadSpecific<int>::Data*>(TlsGetValue(tlsKeys()[i])); + if (data) + data->destructor(data); + } +} + +} // namespace WTF diff --git a/Source/JavaScriptCore/wtf/Threading.cpp b/Source/JavaScriptCore/wtf/Threading.cpp new file mode 100644 index 000000000..f2e0565e9 --- /dev/null +++ b/Source/JavaScriptCore/wtf/Threading.cpp @@ -0,0 +1,100 @@ +/* + * Copyright (C) 2008, 2009 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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 APPLE AND ITS 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 APPLE OR ITS 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 "config.h" +#include "Threading.h" + +#include <string.h> + +namespace WTF { + +struct NewThreadContext { + WTF_MAKE_FAST_ALLOCATED; +public: + NewThreadContext(ThreadFunction entryPoint, void* data, const char* name) + : entryPoint(entryPoint) + , data(data) + , name(name) + { + } + + ThreadFunction entryPoint; + void* data; + const char* name; + + Mutex creationMutex; +}; + +static void* threadEntryPoint(void* contextData) +{ + NewThreadContext* context = reinterpret_cast<NewThreadContext*>(contextData); + + // Block until our creating thread has completed any extra setup work, including + // establishing ThreadIdentifier. + { + MutexLocker locker(context->creationMutex); + } + + initializeCurrentThreadInternal(context->name); + + // Grab the info that we need out of the context, then deallocate it. + ThreadFunction entryPoint = context->entryPoint; + void* data = context->data; + delete context; + + return entryPoint(data); +} + +ThreadIdentifier createThread(ThreadFunction entryPoint, void* data, const char* name) +{ + // Visual Studio has a 31-character limit on thread names. Longer names will + // be truncated silently, but we'd like callers to know about the limit. +#if !LOG_DISABLED + if (strlen(name) > 31) + LOG_ERROR("Thread name \"%s\" is longer than 31 characters and will be truncated by Visual Studio", name); +#endif + + NewThreadContext* context = new NewThreadContext(entryPoint, data, name); + + // Prevent the thread body from executing until we've established the thread identifier. + MutexLocker locker(context->creationMutex); + + return createThreadInternal(threadEntryPoint, context, name); +} + +#if PLATFORM(MAC) || PLATFORM(WIN) + +// This function is deprecated but needs to be kept around for backward +// compatibility. Use the 3-argument version of createThread above. + +ThreadIdentifier createThread(ThreadFunction entryPoint, void* data); + +ThreadIdentifier createThread(ThreadFunction entryPoint, void* data) +{ + return createThread(entryPoint, data, 0); +} +#endif + +} // namespace WTF diff --git a/Source/JavaScriptCore/wtf/Threading.h b/Source/JavaScriptCore/wtf/Threading.h new file mode 100644 index 000000000..3a89757a9 --- /dev/null +++ b/Source/JavaScriptCore/wtf/Threading.h @@ -0,0 +1,117 @@ +/* + * Copyright (C) 2007, 2008, 2010 Apple Inc. All rights reserved. + * Copyright (C) 2007 Justin Haygood (jhaygood@reaktix.com) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of Apple Computer, Inc. ("Apple") 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 APPLE AND ITS 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 APPLE OR ITS 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. + * + * + * Note: The implementations of InterlockedIncrement and InterlockedDecrement are based + * on atomic_increment and atomic_exchange_and_add from the Boost C++ Library. The license + * is virtually identical to the Apple license above but is included here for completeness. + * + * Boost Software License - Version 1.0 - August 17th, 2003 + * + * Permission is hereby granted, free of charge, to any person or organization + * obtaining a copy of the software and accompanying documentation covered by + * this license (the "Software") to use, reproduce, display, distribute, + * execute, and transmit the Software, and to prepare derivative works of the + * Software, and to permit third-parties to whom the Software is furnished to + * do so, all subject to the following: + * + * The copyright notices in the Software and this entire statement, including + * the above license grant, this restriction and the following disclaimer, + * must be included in all copies of the Software, in whole or in part, and + * all derivative works of the Software, unless such copies or derivative + * works are solely in the form of machine-executable object code generated by + * a source language processor. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT + * SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE + * FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#ifndef Threading_h +#define Threading_h + +#include "Platform.h" + +#include <stdint.h> +#include <wtf/Assertions.h> +#include <wtf/Atomics.h> +#include <wtf/Locker.h> +#include <wtf/Noncopyable.h> +#include <wtf/ThreadSafeRefCounted.h> +#include <wtf/ThreadingPrimitives.h> + +// For portability, we do not use thread-safe statics natively supported by some compilers (e.g. gcc). +#define AtomicallyInitializedStatic(T, name) \ + WTF::lockAtomicallyInitializedStaticMutex(); \ + static T name; \ + WTF::unlockAtomicallyInitializedStaticMutex(); + +namespace WTF { + +typedef uint32_t ThreadIdentifier; +typedef void* (*ThreadFunction)(void* argument); + +// This function must be called from the main thread. It is safe to call it repeatedly. +// Darwin is an exception to this rule: it is OK to call it from any thread, the only +// requirement is that the calls are not reentrant. +void initializeThreading(); + +// Returns 0 if thread creation failed. +// The thread name must be a literal since on some platforms it's passed in to the thread. +ThreadIdentifier createThread(ThreadFunction, void*, const char* threadName); + +// Internal platform-specific createThread implementation. +ThreadIdentifier createThreadInternal(ThreadFunction, void*, const char* threadName); + +// Called in the thread during initialization. +// Helpful for platforms where the thread name must be set from within the thread. +void initializeCurrentThreadInternal(const char* threadName); + +ThreadIdentifier currentThread(); +int waitForThreadCompletion(ThreadIdentifier, void**); +void detachThread(ThreadIdentifier); + +void yield(); + +void lockAtomicallyInitializedStaticMutex(); +void unlockAtomicallyInitializedStaticMutex(); + +} // namespace WTF + +using WTF::ThreadIdentifier; +using WTF::createThread; +using WTF::currentThread; +using WTF::detachThread; +using WTF::waitForThreadCompletion; +using WTF::yield; + +#endif // Threading_h diff --git a/Source/JavaScriptCore/wtf/ThreadingNone.cpp b/Source/JavaScriptCore/wtf/ThreadingNone.cpp new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/Source/JavaScriptCore/wtf/ThreadingNone.cpp diff --git a/Source/JavaScriptCore/wtf/ThreadingPrimitives.h b/Source/JavaScriptCore/wtf/ThreadingPrimitives.h new file mode 100644 index 000000000..1bed5d3ae --- /dev/null +++ b/Source/JavaScriptCore/wtf/ThreadingPrimitives.h @@ -0,0 +1,151 @@ +/* + * Copyright (C) 2007, 2008, 2010 Apple Inc. All rights reserved. + * Copyright (C) 2007 Justin Haygood (jhaygood@reaktix.com) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of Apple Computer, Inc. ("Apple") 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 APPLE AND ITS 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 APPLE OR ITS 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. + * + */ + +#ifndef ThreadingPrimitives_h +#define ThreadingPrimitives_h + +#include "Platform.h" + +#include <wtf/Assertions.h> +#include <wtf/FastAllocBase.h> +#include <wtf/Locker.h> +#include <wtf/Noncopyable.h> + +#if OS(WINDOWS) +#include <windows.h> +#endif + +#if USE(PTHREADS) +#include <pthread.h> +#endif + +namespace WTF { + +#if USE(PTHREADS) +typedef pthread_mutex_t PlatformMutex; +#if HAVE(PTHREAD_RWLOCK) +typedef pthread_rwlock_t PlatformReadWriteLock; +#else +typedef void* PlatformReadWriteLock; +#endif +typedef pthread_cond_t PlatformCondition; +#elif OS(WINDOWS) +struct PlatformMutex { + CRITICAL_SECTION m_internalMutex; + size_t m_recursionCount; +}; +typedef void* PlatformReadWriteLock; // FIXME: Implement. +struct PlatformCondition { + size_t m_waitersGone; + size_t m_waitersBlocked; + size_t m_waitersToUnblock; + HANDLE m_blockLock; + HANDLE m_blockQueue; + HANDLE m_unblockLock; + + bool timedWait(PlatformMutex&, DWORD durationMilliseconds); + void signal(bool unblockAll); +}; +#else +typedef void* PlatformMutex; +typedef void* PlatformReadWriteLock; +typedef void* PlatformCondition; +#endif + +class Mutex { + WTF_MAKE_NONCOPYABLE(Mutex); WTF_MAKE_FAST_ALLOCATED; +public: + Mutex(); + ~Mutex(); + + void lock(); + bool tryLock(); + void unlock(); + +public: + PlatformMutex& impl() { return m_mutex; } +private: + PlatformMutex m_mutex; +}; + +typedef Locker<Mutex> MutexLocker; + +class ReadWriteLock { + WTF_MAKE_NONCOPYABLE(ReadWriteLock); +public: + ReadWriteLock(); + ~ReadWriteLock(); + + void readLock(); + bool tryReadLock(); + + void writeLock(); + bool tryWriteLock(); + + void unlock(); + +private: + PlatformReadWriteLock m_readWriteLock; +}; + +class ThreadCondition { + WTF_MAKE_NONCOPYABLE(ThreadCondition); +public: + ThreadCondition(); + ~ThreadCondition(); + + void wait(Mutex& mutex); + // Returns true if the condition was signaled before absoluteTime, false if the absoluteTime was reached or is in the past. + // The absoluteTime is in seconds, starting on January 1, 1970. The time is assumed to use the same time zone as WTF::currentTime(). + bool timedWait(Mutex&, double absoluteTime); + void signal(); + void broadcast(); + +private: + PlatformCondition m_condition; +}; + +#if OS(WINDOWS) +// The absoluteTime is in seconds, starting on January 1, 1970. The time is assumed to use the same time zone as WTF::currentTime(). +// Returns an interval in milliseconds suitable for passing to one of the Win32 wait functions (e.g., ::WaitForSingleObject). +DWORD absoluteTimeToWaitTimeoutInterval(double absoluteTime); +#endif + +} // namespace WTF + +using WTF::Mutex; +using WTF::MutexLocker; +using WTF::ThreadCondition; + +#if OS(WINDOWS) +using WTF::absoluteTimeToWaitTimeoutInterval; +#endif + +#endif // ThreadingPrimitives_h diff --git a/Source/JavaScriptCore/wtf/ThreadingPthreads.cpp b/Source/JavaScriptCore/wtf/ThreadingPthreads.cpp new file mode 100644 index 000000000..763ec2bbb --- /dev/null +++ b/Source/JavaScriptCore/wtf/ThreadingPthreads.cpp @@ -0,0 +1,410 @@ +/* + * Copyright (C) 2007, 2009 Apple Inc. All rights reserved. + * Copyright (C) 2007 Justin Haygood (jhaygood@reaktix.com) + * Copyright (C) 2011 Research In Motion Limited. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of Apple Computer, Inc. ("Apple") 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 APPLE AND ITS 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 APPLE OR ITS 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 "config.h" +#include "Threading.h" + +#if USE(PTHREADS) + +#include "CurrentTime.h" +#include "DateMath.h" +#include "dtoa.h" +#include "dtoa/cached-powers.h" +#include "HashMap.h" +#include "RandomNumberSeed.h" +#include "StdLibExtras.h" +#include "ThreadIdentifierDataPthreads.h" +#include "ThreadSpecific.h" +#include "UnusedParam.h" +#include <wtf/WTFThreadData.h> +#include <errno.h> + +#if !COMPILER(MSVC) +#include <limits.h> +#include <sched.h> +#include <sys/time.h> +#endif + +#if OS(MAC_OS_X) && !defined(BUILDING_ON_LEOPARD) +#include <objc/objc-auto.h> +#endif + +#if PLATFORM(BLACKBERRY) +#include <BlackBerryPlatformMisc.h> +#include <BlackBerryPlatformSettings.h> +#endif + +namespace WTF { + +typedef HashMap<ThreadIdentifier, pthread_t> ThreadMap; + +static Mutex* atomicallyInitializedStaticMutex; + +void clearPthreadHandleForIdentifier(ThreadIdentifier); + +static Mutex& threadMapMutex() +{ + DEFINE_STATIC_LOCAL(Mutex, mutex, ()); + return mutex; +} + +void initializeThreading() +{ + if (atomicallyInitializedStaticMutex) + return; + + WTF::double_conversion::initialize(); + // StringImpl::empty() does not construct its static string in a threadsafe fashion, + // so ensure it has been initialized from here. + StringImpl::empty(); + atomicallyInitializedStaticMutex = new Mutex; + threadMapMutex(); + initializeRandomNumberGenerator(); + ThreadIdentifierData::initializeOnce(); + wtfThreadData(); + s_dtoaP5Mutex = new Mutex; + initializeDates(); +} + +void lockAtomicallyInitializedStaticMutex() +{ + ASSERT(atomicallyInitializedStaticMutex); + atomicallyInitializedStaticMutex->lock(); +} + +void unlockAtomicallyInitializedStaticMutex() +{ + atomicallyInitializedStaticMutex->unlock(); +} + +static ThreadMap& threadMap() +{ + DEFINE_STATIC_LOCAL(ThreadMap, map, ()); + return map; +} + +static ThreadIdentifier identifierByPthreadHandle(const pthread_t& pthreadHandle) +{ + MutexLocker locker(threadMapMutex()); + + ThreadMap::iterator i = threadMap().begin(); + for (; i != threadMap().end(); ++i) { + if (pthread_equal(i->second, pthreadHandle)) + return i->first; + } + + return 0; +} + +static ThreadIdentifier establishIdentifierForPthreadHandle(const pthread_t& pthreadHandle) +{ + ASSERT(!identifierByPthreadHandle(pthreadHandle)); + + MutexLocker locker(threadMapMutex()); + + static ThreadIdentifier identifierCount = 1; + + threadMap().add(identifierCount, pthreadHandle); + + return identifierCount++; +} + +static pthread_t pthreadHandleForIdentifier(ThreadIdentifier id) +{ + MutexLocker locker(threadMapMutex()); + + return threadMap().get(id); +} + +void clearPthreadHandleForIdentifier(ThreadIdentifier id) +{ + MutexLocker locker(threadMapMutex()); + + ASSERT(threadMap().contains(id)); + + threadMap().remove(id); +} + +#if PLATFORM(BLACKBERRY) +ThreadIdentifier createThreadInternal(ThreadFunction entryPoint, void* data, const char* threadName) +{ + pthread_attr_t attr; + if (pthread_attr_init(&attr)) { + LOG_ERROR("pthread_attr_init() failed: %d", errno); + return 0; + } + + void* stackAddr; + size_t stackSize; + if (pthread_attr_getstack(&attr, &stackAddr, &stackSize)) + LOG_ERROR("pthread_attr_getstack() failed: %d", errno); + else { + stackSize = BlackBerry::Platform::Settings::get()->secondaryThreadStackSize(); + if (pthread_attr_setstack(&attr, stackAddr, stackSize)) + LOG_ERROR("pthread_attr_getstack() failed: %d", errno); + } + + pthread_t threadHandle; + if (pthread_create(&threadHandle, &attr, entryPoint, data)) { + LOG_ERROR("pthread_create() failed: %d", errno); + threadHandle = 0; + } + pthread_setname_np(threadHandle, threadName); + + pthread_attr_destroy(&attr); + + if (!threadHandle) + return 0; + + return establishIdentifierForPthreadHandle(threadHandle); +} +#else +ThreadIdentifier createThreadInternal(ThreadFunction entryPoint, void* data, const char*) +{ + pthread_t threadHandle; + if (pthread_create(&threadHandle, 0, entryPoint, data)) { + LOG_ERROR("Failed to create pthread at entry point %p with data %p", entryPoint, data); + return 0; + } + + return establishIdentifierForPthreadHandle(threadHandle); +} +#endif + +void initializeCurrentThreadInternal(const char* threadName) +{ +#if HAVE(PTHREAD_SETNAME_NP) + pthread_setname_np(threadName); +#else + UNUSED_PARAM(threadName); +#endif + +#if OS(MAC_OS_X) && !defined(BUILDING_ON_LEOPARD) + // All threads that potentially use APIs above the BSD layer must be registered with the Objective-C + // garbage collector in case API implementations use garbage-collected memory. + objc_registerThreadWithCollector(); +#endif + + ThreadIdentifier id = identifierByPthreadHandle(pthread_self()); + ASSERT(id); + ThreadIdentifierData::initialize(id); +} + +int waitForThreadCompletion(ThreadIdentifier threadID, void** result) +{ + ASSERT(threadID); + + pthread_t pthreadHandle = pthreadHandleForIdentifier(threadID); + if (!pthreadHandle) + return 0; + + int joinResult = pthread_join(pthreadHandle, result); + if (joinResult == EDEADLK) + LOG_ERROR("ThreadIdentifier %u was found to be deadlocked trying to quit", threadID); + + return joinResult; +} + +void detachThread(ThreadIdentifier threadID) +{ + ASSERT(threadID); + + pthread_t pthreadHandle = pthreadHandleForIdentifier(threadID); + if (!pthreadHandle) + return; + + pthread_detach(pthreadHandle); +} + +void yield() +{ + sched_yield(); +} + +ThreadIdentifier currentThread() +{ + ThreadIdentifier id = ThreadIdentifierData::identifier(); + if (id) + return id; + + // Not a WTF-created thread, ThreadIdentifier is not established yet. + id = establishIdentifierForPthreadHandle(pthread_self()); + ThreadIdentifierData::initialize(id); + return id; +} + +Mutex::Mutex() +{ + pthread_mutexattr_t attr; + pthread_mutexattr_init(&attr); + pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_NORMAL); + + pthread_mutex_init(&m_mutex, &attr); + + pthread_mutexattr_destroy(&attr); +} + +Mutex::~Mutex() +{ + pthread_mutex_destroy(&m_mutex); +} + +void Mutex::lock() +{ + int result = pthread_mutex_lock(&m_mutex); + ASSERT_UNUSED(result, !result); +} + +bool Mutex::tryLock() +{ + int result = pthread_mutex_trylock(&m_mutex); + + if (result == 0) + return true; + if (result == EBUSY) + return false; + + ASSERT_NOT_REACHED(); + return false; +} + +void Mutex::unlock() +{ + int result = pthread_mutex_unlock(&m_mutex); + ASSERT_UNUSED(result, !result); +} + +#if HAVE(PTHREAD_RWLOCK) +ReadWriteLock::ReadWriteLock() +{ + pthread_rwlock_init(&m_readWriteLock, NULL); +} + +ReadWriteLock::~ReadWriteLock() +{ + pthread_rwlock_destroy(&m_readWriteLock); +} + +void ReadWriteLock::readLock() +{ + int result = pthread_rwlock_rdlock(&m_readWriteLock); + ASSERT_UNUSED(result, !result); +} + +bool ReadWriteLock::tryReadLock() +{ + int result = pthread_rwlock_tryrdlock(&m_readWriteLock); + + if (result == 0) + return true; + if (result == EBUSY || result == EAGAIN) + return false; + + ASSERT_NOT_REACHED(); + return false; +} + +void ReadWriteLock::writeLock() +{ + int result = pthread_rwlock_wrlock(&m_readWriteLock); + ASSERT_UNUSED(result, !result); +} + +bool ReadWriteLock::tryWriteLock() +{ + int result = pthread_rwlock_trywrlock(&m_readWriteLock); + + if (result == 0) + return true; + if (result == EBUSY || result == EAGAIN) + return false; + + ASSERT_NOT_REACHED(); + return false; +} + +void ReadWriteLock::unlock() +{ + int result = pthread_rwlock_unlock(&m_readWriteLock); + ASSERT_UNUSED(result, !result); +} +#endif // HAVE(PTHREAD_RWLOCK) + +ThreadCondition::ThreadCondition() +{ + pthread_cond_init(&m_condition, NULL); +} + +ThreadCondition::~ThreadCondition() +{ + pthread_cond_destroy(&m_condition); +} + +void ThreadCondition::wait(Mutex& mutex) +{ + int result = pthread_cond_wait(&m_condition, &mutex.impl()); + ASSERT_UNUSED(result, !result); +} + +bool ThreadCondition::timedWait(Mutex& mutex, double absoluteTime) +{ + if (absoluteTime < currentTime()) + return false; + + if (absoluteTime > INT_MAX) { + wait(mutex); + return true; + } + + int timeSeconds = static_cast<int>(absoluteTime); + int timeNanoseconds = static_cast<int>((absoluteTime - timeSeconds) * 1E9); + + timespec targetTime; + targetTime.tv_sec = timeSeconds; + targetTime.tv_nsec = timeNanoseconds; + + return pthread_cond_timedwait(&m_condition, &mutex.impl(), &targetTime) == 0; +} + +void ThreadCondition::signal() +{ + int result = pthread_cond_signal(&m_condition); + ASSERT_UNUSED(result, !result); +} + +void ThreadCondition::broadcast() +{ + int result = pthread_cond_broadcast(&m_condition); + ASSERT_UNUSED(result, !result); +} + +} // namespace WTF + +#endif // USE(PTHREADS) diff --git a/Source/JavaScriptCore/wtf/ThreadingWin.cpp b/Source/JavaScriptCore/wtf/ThreadingWin.cpp new file mode 100644 index 000000000..ac0f73f19 --- /dev/null +++ b/Source/JavaScriptCore/wtf/ThreadingWin.cpp @@ -0,0 +1,515 @@ +/* + * Copyright (C) 2007, 2008 Apple Inc. All rights reserved. + * Copyright (C) 2009 Google Inc. All rights reserved. + * Copyright (C) 2009 Torch Mobile, Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of Apple Computer, Inc. ("Apple") 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 APPLE AND ITS 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 APPLE OR ITS 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. + */ + +/* + * There are numerous academic and practical works on how to implement pthread_cond_wait/pthread_cond_signal/pthread_cond_broadcast + * functions on Win32. Here is one example: http://www.cs.wustl.edu/~schmidt/win32-cv-1.html which is widely credited as a 'starting point' + * of modern attempts. There are several more or less proven implementations, one in Boost C++ library (http://www.boost.org) and another + * in pthreads-win32 (http://sourceware.org/pthreads-win32/). + * + * The number of articles and discussions is the evidence of significant difficulties in implementing these primitives correctly. + * The brief search of revisions, ChangeLog entries, discussions in comp.programming.threads and other places clearly documents + * numerous pitfalls and performance problems the authors had to overcome to arrive to the suitable implementations. + * Optimally, WebKit would use one of those supported/tested libraries directly. To roll out our own implementation is impractical, + * if even for the lack of sufficient testing. However, a faithful reproduction of the code from one of the popular supported + * libraries seems to be a good compromise. + * + * The early Boost implementation (http://www.boxbackup.org/trac/browser/box/nick/win/lib/win32/boost_1_32_0/libs/thread/src/condition.cpp?rev=30) + * is identical to pthreads-win32 (http://sourceware.org/cgi-bin/cvsweb.cgi/pthreads/pthread_cond_wait.c?rev=1.10&content-type=text/x-cvsweb-markup&cvsroot=pthreads-win32). + * Current Boost uses yet another (although seemingly equivalent) algorithm which came from their 'thread rewrite' effort. + * + * This file includes timedWait/signal/broadcast implementations translated to WebKit coding style from the latest algorithm by + * Alexander Terekhov and Louis Thomas, as captured here: http://sourceware.org/cgi-bin/cvsweb.cgi/pthreads/pthread_cond_wait.c?rev=1.10&content-type=text/x-cvsweb-markup&cvsroot=pthreads-win32 + * It replaces the implementation of their previous algorithm, also documented in the same source above. + * The naming and comments are left very close to original to enable easy cross-check. + * + * The corresponding Pthreads-win32 License is included below, and CONTRIBUTORS file which it refers to is added to + * source directory (as CONTRIBUTORS.pthreads-win32). + */ + +/* + * Pthreads-win32 - POSIX Threads Library for Win32 + * Copyright(C) 1998 John E. Bossom + * Copyright(C) 1999,2005 Pthreads-win32 contributors + * + * Contact Email: rpj@callisto.canberra.edu.au + * + * The current list of contributors is contained + * in the file CONTRIBUTORS included with the source + * code distribution. The list can also be seen at the + * following World Wide Web location: + * http://sources.redhat.com/pthreads-win32/contributors.html + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library in the file COPYING.LIB; + * if not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ + +#include "config.h" +#include "Threading.h" +#include "DateMath.h" +#include "dtoa.h" +#include "dtoa/cached-powers.h" + +#include "MainThread.h" +#include "ThreadFunctionInvocation.h" +#include <windows.h> +#include <wtf/CurrentTime.h> +#include <wtf/HashMap.h> +#include <wtf/MathExtras.h> +#include <wtf/OwnPtr.h> +#include <wtf/PassOwnPtr.h> +#include <wtf/RandomNumberSeed.h> +#include <wtf/WTFThreadData.h> + +#if !USE(PTHREADS) && OS(WINDOWS) +#include "ThreadSpecific.h" +#endif + +#if !OS(WINCE) +#include <process.h> +#endif + +#if HAVE(ERRNO_H) +#include <errno.h> +#endif + +namespace WTF { + +// MS_VC_EXCEPTION, THREADNAME_INFO, and setThreadNameInternal all come from <http://msdn.microsoft.com/en-us/library/xcb2z8hs.aspx>. +static const DWORD MS_VC_EXCEPTION = 0x406D1388; + +#pragma pack(push, 8) +typedef struct tagTHREADNAME_INFO { + DWORD dwType; // must be 0x1000 + LPCSTR szName; // pointer to name (in user addr space) + DWORD dwThreadID; // thread ID (-1=caller thread) + DWORD dwFlags; // reserved for future use, must be zero +} THREADNAME_INFO; +#pragma pack(pop) + +void initializeCurrentThreadInternal(const char* szThreadName) +{ +#if COMPILER(MINGW) + // FIXME: Implement thread name setting with MingW. + UNUSED_PARAM(szThreadName); +#else + THREADNAME_INFO info; + info.dwType = 0x1000; + info.szName = szThreadName; + info.dwThreadID = GetCurrentThreadId(); + info.dwFlags = 0; + + __try { + RaiseException(MS_VC_EXCEPTION, 0, sizeof(info)/sizeof(ULONG_PTR), reinterpret_cast<ULONG_PTR*>(&info)); + } __except (EXCEPTION_CONTINUE_EXECUTION) { + } +#endif +} + +static Mutex* atomicallyInitializedStaticMutex; + +void lockAtomicallyInitializedStaticMutex() +{ + ASSERT(atomicallyInitializedStaticMutex); + atomicallyInitializedStaticMutex->lock(); +} + +void unlockAtomicallyInitializedStaticMutex() +{ + atomicallyInitializedStaticMutex->unlock(); +} + +static Mutex& threadMapMutex() +{ + static Mutex mutex; + return mutex; +} + +void initializeThreading() +{ + if (atomicallyInitializedStaticMutex) + return; + + WTF::double_conversion::initialize(); + // StringImpl::empty() does not construct its static string in a threadsafe fashion, + // so ensure it has been initialized from here. + StringImpl::empty(); + atomicallyInitializedStaticMutex = new Mutex; + threadMapMutex(); + initializeRandomNumberGenerator(); + wtfThreadData(); + s_dtoaP5Mutex = new Mutex; + initializeDates(); +} + +static HashMap<DWORD, HANDLE>& threadMap() +{ + static HashMap<DWORD, HANDLE> map; + return map; +} + +static void storeThreadHandleByIdentifier(DWORD threadID, HANDLE threadHandle) +{ + MutexLocker locker(threadMapMutex()); + ASSERT(!threadMap().contains(threadID)); + threadMap().add(threadID, threadHandle); +} + +static HANDLE threadHandleForIdentifier(ThreadIdentifier id) +{ + MutexLocker locker(threadMapMutex()); + return threadMap().get(id); +} + +static void clearThreadHandleForIdentifier(ThreadIdentifier id) +{ + MutexLocker locker(threadMapMutex()); + ASSERT(threadMap().contains(id)); + threadMap().remove(id); +} + +static unsigned __stdcall wtfThreadEntryPoint(void* param) +{ + OwnPtr<ThreadFunctionInvocation> invocation = adoptPtr(static_cast<ThreadFunctionInvocation*>(param)); + void* result = invocation->function(invocation->data); + +#if !USE(PTHREADS) && OS(WINDOWS) + // Do the TLS cleanup. + ThreadSpecificThreadExit(); +#endif + + return reinterpret_cast<unsigned>(result); +} + +ThreadIdentifier createThreadInternal(ThreadFunction entryPoint, void* data, const char* threadName) +{ + unsigned threadIdentifier = 0; + ThreadIdentifier threadID = 0; + OwnPtr<ThreadFunctionInvocation> invocation = adoptPtr(new ThreadFunctionInvocation(entryPoint, data)); +#if OS(WINCE) + // This is safe on WINCE, since CRT is in the core and innately multithreaded. + // On desktop Windows, need to use _beginthreadex (not available on WinCE) if using any CRT functions + HANDLE threadHandle = CreateThread(0, 0, (LPTHREAD_START_ROUTINE)wtfThreadEntryPoint, invocation.get(), 0, (LPDWORD)&threadIdentifier); +#else + HANDLE threadHandle = reinterpret_cast<HANDLE>(_beginthreadex(0, 0, wtfThreadEntryPoint, invocation.get(), 0, &threadIdentifier)); +#endif + if (!threadHandle) { +#if OS(WINCE) + LOG_ERROR("Failed to create thread at entry point %p with data %p: %ld", entryPoint, data, ::GetLastError()); +#elif !HAVE(ERRNO_H) + LOG_ERROR("Failed to create thread at entry point %p with data %p.", entryPoint, data); +#else + LOG_ERROR("Failed to create thread at entry point %p with data %p: %ld", entryPoint, data, errno); +#endif + return 0; + } + + // The thread will take ownership of invocation. + invocation.leakPtr(); + + threadID = static_cast<ThreadIdentifier>(threadIdentifier); + storeThreadHandleByIdentifier(threadIdentifier, threadHandle); + + return threadID; +} + +int waitForThreadCompletion(ThreadIdentifier threadID, void** result) +{ + ASSERT(threadID); + + HANDLE threadHandle = threadHandleForIdentifier(threadID); + if (!threadHandle) + LOG_ERROR("ThreadIdentifier %u did not correspond to an active thread when trying to quit", threadID); + + DWORD joinResult = WaitForSingleObject(threadHandle, INFINITE); + if (joinResult == WAIT_FAILED) + LOG_ERROR("ThreadIdentifier %u was found to be deadlocked trying to quit", threadID); + + CloseHandle(threadHandle); + clearThreadHandleForIdentifier(threadID); + + return joinResult; +} + +void detachThread(ThreadIdentifier threadID) +{ + ASSERT(threadID); + + HANDLE threadHandle = threadHandleForIdentifier(threadID); + if (threadHandle) + CloseHandle(threadHandle); + clearThreadHandleForIdentifier(threadID); +} + +void yield() +{ + ::Sleep(1); +} + +ThreadIdentifier currentThread() +{ + return static_cast<ThreadIdentifier>(GetCurrentThreadId()); +} + +Mutex::Mutex() +{ + m_mutex.m_recursionCount = 0; + InitializeCriticalSection(&m_mutex.m_internalMutex); +} + +Mutex::~Mutex() +{ + DeleteCriticalSection(&m_mutex.m_internalMutex); +} + +void Mutex::lock() +{ + EnterCriticalSection(&m_mutex.m_internalMutex); + ++m_mutex.m_recursionCount; +} + +bool Mutex::tryLock() +{ + // This method is modeled after the behavior of pthread_mutex_trylock, + // which will return an error if the lock is already owned by the + // current thread. Since the primitive Win32 'TryEnterCriticalSection' + // treats this as a successful case, it changes the behavior of several + // tests in WebKit that check to see if the current thread already + // owned this mutex (see e.g., IconDatabase::getOrCreateIconRecord) + DWORD result = TryEnterCriticalSection(&m_mutex.m_internalMutex); + + if (result != 0) { // We got the lock + // If this thread already had the lock, we must unlock and + // return false so that we mimic the behavior of POSIX's + // pthread_mutex_trylock: + if (m_mutex.m_recursionCount > 0) { + LeaveCriticalSection(&m_mutex.m_internalMutex); + return false; + } + + ++m_mutex.m_recursionCount; + return true; + } + + return false; +} + +void Mutex::unlock() +{ + --m_mutex.m_recursionCount; + LeaveCriticalSection(&m_mutex.m_internalMutex); +} + +bool PlatformCondition::timedWait(PlatformMutex& mutex, DWORD durationMilliseconds) +{ + // Enter the wait state. + DWORD res = WaitForSingleObject(m_blockLock, INFINITE); + ASSERT(res == WAIT_OBJECT_0); + ++m_waitersBlocked; + res = ReleaseSemaphore(m_blockLock, 1, 0); + ASSERT(res); + + --mutex.m_recursionCount; + LeaveCriticalSection(&mutex.m_internalMutex); + + // Main wait - use timeout. + bool timedOut = (WaitForSingleObject(m_blockQueue, durationMilliseconds) == WAIT_TIMEOUT); + + res = WaitForSingleObject(m_unblockLock, INFINITE); + ASSERT(res == WAIT_OBJECT_0); + + int signalsLeft = m_waitersToUnblock; + + if (m_waitersToUnblock) + --m_waitersToUnblock; + else if (++m_waitersGone == (INT_MAX / 2)) { // timeout/canceled or spurious semaphore + // timeout or spurious wakeup occured, normalize the m_waitersGone count + // this may occur if many calls to wait with a timeout are made and + // no call to notify_* is made + res = WaitForSingleObject(m_blockLock, INFINITE); + ASSERT(res == WAIT_OBJECT_0); + m_waitersBlocked -= m_waitersGone; + res = ReleaseSemaphore(m_blockLock, 1, 0); + ASSERT(res); + m_waitersGone = 0; + } + + res = ReleaseMutex(m_unblockLock); + ASSERT(res); + + if (signalsLeft == 1) { + res = ReleaseSemaphore(m_blockLock, 1, 0); // Open the gate. + ASSERT(res); + } + + EnterCriticalSection (&mutex.m_internalMutex); + ++mutex.m_recursionCount; + + return !timedOut; +} + +void PlatformCondition::signal(bool unblockAll) +{ + unsigned signalsToIssue = 0; + + DWORD res = WaitForSingleObject(m_unblockLock, INFINITE); + ASSERT(res == WAIT_OBJECT_0); + + if (m_waitersToUnblock) { // the gate is already closed + if (!m_waitersBlocked) { // no-op + res = ReleaseMutex(m_unblockLock); + ASSERT(res); + return; + } + + if (unblockAll) { + signalsToIssue = m_waitersBlocked; + m_waitersToUnblock += m_waitersBlocked; + m_waitersBlocked = 0; + } else { + signalsToIssue = 1; + ++m_waitersToUnblock; + --m_waitersBlocked; + } + } else if (m_waitersBlocked > m_waitersGone) { + res = WaitForSingleObject(m_blockLock, INFINITE); // Close the gate. + ASSERT(res == WAIT_OBJECT_0); + if (m_waitersGone != 0) { + m_waitersBlocked -= m_waitersGone; + m_waitersGone = 0; + } + if (unblockAll) { + signalsToIssue = m_waitersBlocked; + m_waitersToUnblock = m_waitersBlocked; + m_waitersBlocked = 0; + } else { + signalsToIssue = 1; + m_waitersToUnblock = 1; + --m_waitersBlocked; + } + } else { // No-op. + res = ReleaseMutex(m_unblockLock); + ASSERT(res); + return; + } + + res = ReleaseMutex(m_unblockLock); + ASSERT(res); + + if (signalsToIssue) { + res = ReleaseSemaphore(m_blockQueue, signalsToIssue, 0); + ASSERT(res); + } +} + +static const long MaxSemaphoreCount = static_cast<long>(~0UL >> 1); + +ThreadCondition::ThreadCondition() +{ + m_condition.m_waitersGone = 0; + m_condition.m_waitersBlocked = 0; + m_condition.m_waitersToUnblock = 0; + m_condition.m_blockLock = CreateSemaphore(0, 1, 1, 0); + m_condition.m_blockQueue = CreateSemaphore(0, 0, MaxSemaphoreCount, 0); + m_condition.m_unblockLock = CreateMutex(0, 0, 0); + + if (!m_condition.m_blockLock || !m_condition.m_blockQueue || !m_condition.m_unblockLock) { + if (m_condition.m_blockLock) + CloseHandle(m_condition.m_blockLock); + if (m_condition.m_blockQueue) + CloseHandle(m_condition.m_blockQueue); + if (m_condition.m_unblockLock) + CloseHandle(m_condition.m_unblockLock); + } +} + +ThreadCondition::~ThreadCondition() +{ + CloseHandle(m_condition.m_blockLock); + CloseHandle(m_condition.m_blockQueue); + CloseHandle(m_condition.m_unblockLock); +} + +void ThreadCondition::wait(Mutex& mutex) +{ + m_condition.timedWait(mutex.impl(), INFINITE); +} + +bool ThreadCondition::timedWait(Mutex& mutex, double absoluteTime) +{ + DWORD interval = absoluteTimeToWaitTimeoutInterval(absoluteTime); + + if (!interval) { + // Consider the wait to have timed out, even if our condition has already been signaled, to + // match the pthreads implementation. + return false; + } + + return m_condition.timedWait(mutex.impl(), interval); +} + +void ThreadCondition::signal() +{ + m_condition.signal(false); // Unblock only 1 thread. +} + +void ThreadCondition::broadcast() +{ + m_condition.signal(true); // Unblock all threads. +} + +DWORD absoluteTimeToWaitTimeoutInterval(double absoluteTime) +{ + double currentTime = WTF::currentTime(); + + // Time is in the past - return immediately. + if (absoluteTime < currentTime) + return 0; + + // Time is too far in the future (and would overflow unsigned long) - wait forever. + if (absoluteTime - currentTime > static_cast<double>(INT_MAX) / 1000.0) + return INFINITE; + + return static_cast<DWORD>((absoluteTime - currentTime) * 1000.0); +} + +} // namespace WTF diff --git a/Source/JavaScriptCore/wtf/TypeTraits.cpp b/Source/JavaScriptCore/wtf/TypeTraits.cpp new file mode 100644 index 000000000..afeaa5e4c --- /dev/null +++ b/Source/JavaScriptCore/wtf/TypeTraits.cpp @@ -0,0 +1,142 @@ + /* + * Copyright (C) 2006, 2007, 2008 Apple Inc. All rights reserved. + * Copyright (C) 2009, 2010 Google Inc. All rights reserved. + * + * This library 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 library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#include "config.h" +#include "TypeTraits.h" + +#include "Assertions.h" + +namespace WTF { + +COMPILE_ASSERT(IsInteger<bool>::value, WTF_IsInteger_bool_true); +COMPILE_ASSERT(IsInteger<char>::value, WTF_IsInteger_char_true); +COMPILE_ASSERT(IsInteger<signed char>::value, WTF_IsInteger_signed_char_true); +COMPILE_ASSERT(IsInteger<unsigned char>::value, WTF_IsInteger_unsigned_char_true); +COMPILE_ASSERT(IsInteger<short>::value, WTF_IsInteger_short_true); +COMPILE_ASSERT(IsInteger<unsigned short>::value, WTF_IsInteger_unsigned_short_true); +COMPILE_ASSERT(IsInteger<int>::value, WTF_IsInteger_int_true); +COMPILE_ASSERT(IsInteger<unsigned int>::value, WTF_IsInteger_unsigned_int_true); +COMPILE_ASSERT(IsInteger<long>::value, WTF_IsInteger_long_true); +COMPILE_ASSERT(IsInteger<unsigned long>::value, WTF_IsInteger_unsigned_long_true); +COMPILE_ASSERT(IsInteger<long long>::value, WTF_IsInteger_long_long_true); +COMPILE_ASSERT(IsInteger<unsigned long long>::value, WTF_IsInteger_unsigned_long_long_true); +#if !COMPILER(MSVC) || defined(_NATIVE_WCHAR_T_DEFINED) +COMPILE_ASSERT(IsInteger<wchar_t>::value, WTF_IsInteger_wchar_t_true); +#endif +COMPILE_ASSERT(!IsInteger<char*>::value, WTF_IsInteger_char_pointer_false); +COMPILE_ASSERT(!IsInteger<const char*>::value, WTF_IsInteger_const_char_pointer_false); +COMPILE_ASSERT(!IsInteger<volatile char*>::value, WTF_IsInteger_volatile_char_pointer_false); +COMPILE_ASSERT(!IsInteger<double>::value, WTF_IsInteger_double_false); +COMPILE_ASSERT(!IsInteger<float>::value, WTF_IsInteger_float_false); + +COMPILE_ASSERT(IsFloatingPoint<float>::value, WTF_IsFloatingPoint_float_true); +COMPILE_ASSERT(IsFloatingPoint<double>::value, WTF_IsFloatingPoint_double_true); +COMPILE_ASSERT(IsFloatingPoint<long double>::value, WTF_IsFloatingPoint_long_double_true); +COMPILE_ASSERT(!IsFloatingPoint<int>::value, WTF_IsFloatingPoint_int_false); + +COMPILE_ASSERT(IsPod<bool>::value, WTF_IsPod_bool_true); +COMPILE_ASSERT(IsPod<char>::value, WTF_IsPod_char_true); +COMPILE_ASSERT(IsPod<signed char>::value, WTF_IsPod_signed_char_true); +COMPILE_ASSERT(IsPod<unsigned char>::value, WTF_IsPod_unsigned_char_true); +COMPILE_ASSERT(IsPod<short>::value, WTF_IsPod_short_true); +COMPILE_ASSERT(IsPod<unsigned short>::value, WTF_IsPod_unsigned_short_true); +COMPILE_ASSERT(IsPod<int>::value, WTF_IsPod_int_true); +COMPILE_ASSERT(IsPod<unsigned int>::value, WTF_IsPod_unsigned_int_true); +COMPILE_ASSERT(IsPod<long>::value, WTF_IsPod_long_true); +COMPILE_ASSERT(IsPod<unsigned long>::value, WTF_IsPod_unsigned_long_true); +COMPILE_ASSERT(IsPod<long long>::value, WTF_IsPod_long_long_true); +COMPILE_ASSERT(IsPod<unsigned long long>::value, WTF_IsPod_unsigned_long_long_true); +#if !COMPILER(MSVC) || defined(_NATIVE_WCHAR_T_DEFINED) +COMPILE_ASSERT(IsPod<wchar_t>::value, WTF_IsPod_wchar_t_true); +#endif +COMPILE_ASSERT(IsPod<char*>::value, WTF_IsPod_char_pointer_true); +COMPILE_ASSERT(IsPod<const char*>::value, WTF_IsPod_const_char_pointer_true); +COMPILE_ASSERT(IsPod<volatile char*>::value, WTF_IsPod_volatile_char_pointer_true); +COMPILE_ASSERT(IsPod<double>::value, WTF_IsPod_double_true); +COMPILE_ASSERT(IsPod<long double>::value, WTF_IsPod_long_double_true); +COMPILE_ASSERT(IsPod<float>::value, WTF_IsPod_float_true); +COMPILE_ASSERT(!IsPod<IsPod<bool> >::value, WTF_IsPod_struct_false); + +enum IsConvertibleToIntegerCheck { }; +COMPILE_ASSERT(IsConvertibleToInteger<IsConvertibleToIntegerCheck>::value, WTF_IsConvertibleToInteger_enum_true); +COMPILE_ASSERT(IsConvertibleToInteger<bool>::value, WTF_IsConvertibleToInteger_bool_true); +COMPILE_ASSERT(IsConvertibleToInteger<char>::value, WTF_IsConvertibleToInteger_char_true); +COMPILE_ASSERT(IsConvertibleToInteger<signed char>::value, WTF_IsConvertibleToInteger_signed_char_true); +COMPILE_ASSERT(IsConvertibleToInteger<unsigned char>::value, WTF_IsConvertibleToInteger_unsigned_char_true); +COMPILE_ASSERT(IsConvertibleToInteger<short>::value, WTF_IsConvertibleToInteger_short_true); +COMPILE_ASSERT(IsConvertibleToInteger<unsigned short>::value, WTF_IsConvertibleToInteger_unsigned_short_true); +COMPILE_ASSERT(IsConvertibleToInteger<int>::value, WTF_IsConvertibleToInteger_int_true); +COMPILE_ASSERT(IsConvertibleToInteger<unsigned int>::value, WTF_IsConvertibleToInteger_unsigned_int_true); +COMPILE_ASSERT(IsConvertibleToInteger<long>::value, WTF_IsConvertibleToInteger_long_true); +COMPILE_ASSERT(IsConvertibleToInteger<unsigned long>::value, WTF_IsConvertibleToInteger_unsigned_long_true); +COMPILE_ASSERT(IsConvertibleToInteger<long long>::value, WTF_IsConvertibleToInteger_long_long_true); +COMPILE_ASSERT(IsConvertibleToInteger<unsigned long long>::value, WTF_IsConvertibleToInteger_unsigned_long_long_true); +#if !COMPILER(MSVC) || defined(_NATIVE_WCHAR_T_DEFINED) +COMPILE_ASSERT(IsConvertibleToInteger<wchar_t>::value, WTF_IsConvertibleToInteger_wchar_t_true); +#endif +COMPILE_ASSERT(IsConvertibleToInteger<double>::value, WTF_IsConvertibleToInteger_double_true); +COMPILE_ASSERT(IsConvertibleToInteger<long double>::value, WTF_IsConvertibleToInteger_long_double_true); +COMPILE_ASSERT(IsConvertibleToInteger<float>::value, WTF_IsConvertibleToInteger_float_true); +COMPILE_ASSERT(!IsConvertibleToInteger<char*>::value, WTF_IsConvertibleToInteger_char_pointer_false); +COMPILE_ASSERT(!IsConvertibleToInteger<const char*>::value, WTF_IsConvertibleToInteger_const_char_pointer_false); +COMPILE_ASSERT(!IsConvertibleToInteger<volatile char*>::value, WTF_IsConvertibleToInteger_volatile_char_pointer_false); +COMPILE_ASSERT(!IsConvertibleToInteger<IsConvertibleToInteger<bool> >::value, WTF_IsConvertibleToInteger_struct_false); + +COMPILE_ASSERT((IsSameType<bool, bool>::value), WTF_IsSameType_bool_true); +COMPILE_ASSERT((IsSameType<int*, int*>::value), WTF_IsSameType_int_pointer_true); +COMPILE_ASSERT((!IsSameType<int, int*>::value), WTF_IsSameType_int_int_pointer_false); +COMPILE_ASSERT((!IsSameType<bool, const bool>::value), WTF_IsSameType_const_change_false); +COMPILE_ASSERT((!IsSameType<bool, volatile bool>::value), WTF_IsSameType_volatile_change_false); + +template <typename T> +class TestBaseClass { +}; + +class TestDerivedClass : public TestBaseClass<int> { +}; + +COMPILE_ASSERT((IsSubclass<TestDerivedClass, TestBaseClass<int> >::value), WTF_Test_IsSubclass_Derived_From_Base); +COMPILE_ASSERT((!IsSubclass<TestBaseClass<int>, TestDerivedClass>::value), WTF_Test_IsSubclass_Base_From_Derived); +COMPILE_ASSERT((IsSubclassOfTemplate<TestDerivedClass, TestBaseClass>::value), WTF_Test_IsSubclassOfTemplate_Base_From_Derived); +COMPILE_ASSERT((IsSameType<RemoveTemplate<TestBaseClass<int>, TestBaseClass>::Type, int>::value), WTF_Test_RemoveTemplate); +COMPILE_ASSERT((IsSameType<RemoveTemplate<int, TestBaseClass>::Type, int>::value), WTF_Test_RemoveTemplate_WithoutTemplate); + + +COMPILE_ASSERT((IsSameType<bool, RemoveConst<const bool>::Type>::value), WTF_test_RemoveConst_const_bool); +COMPILE_ASSERT((!IsSameType<bool, RemoveConst<volatile bool>::Type>::value), WTF_test_RemoveConst_volatile_bool); + +COMPILE_ASSERT((IsSameType<bool, RemoveVolatile<bool>::Type>::value), WTF_test_RemoveVolatile_bool); +COMPILE_ASSERT((!IsSameType<bool, RemoveVolatile<const bool>::Type>::value), WTF_test_RemoveVolatile_const_bool); +COMPILE_ASSERT((IsSameType<bool, RemoveVolatile<volatile bool>::Type>::value), WTF_test_RemoveVolatile_volatile_bool); + +COMPILE_ASSERT((IsSameType<bool, RemoveConstVolatile<bool>::Type>::value), WTF_test_RemoveConstVolatile_bool); +COMPILE_ASSERT((IsSameType<bool, RemoveConstVolatile<const bool>::Type>::value), WTF_test_RemoveConstVolatile_const_bool); +COMPILE_ASSERT((IsSameType<bool, RemoveConstVolatile<volatile bool>::Type>::value), WTF_test_RemoveConstVolatile_volatile_bool); +COMPILE_ASSERT((IsSameType<bool, RemoveConstVolatile<const volatile bool>::Type>::value), WTF_test_RemoveConstVolatile_const_volatile_bool); + +COMPILE_ASSERT((IsSameType<int, RemovePointer<int>::Type>::value), WTF_Test_RemovePointer_int); +COMPILE_ASSERT((IsSameType<int, RemovePointer<int*>::Type>::value), WTF_Test_RemovePointer_int_pointer); +COMPILE_ASSERT((!IsSameType<int, RemovePointer<int**>::Type>::value), WTF_Test_RemovePointer_int_pointer_pointer); + +COMPILE_ASSERT((IsSameType<int, RemoveReference<int>::Type>::value), WTF_Test_RemoveReference_int); +COMPILE_ASSERT((IsSameType<int, RemoveReference<int&>::Type>::value), WTF_Test_RemoveReference_int_reference); + +} // namespace WTF diff --git a/Source/JavaScriptCore/wtf/TypeTraits.h b/Source/JavaScriptCore/wtf/TypeTraits.h new file mode 100644 index 000000000..6c7466acc --- /dev/null +++ b/Source/JavaScriptCore/wtf/TypeTraits.h @@ -0,0 +1,389 @@ + /* + * Copyright (C) 2006, 2007, 2008 Apple Inc. All rights reserved. + * Copyright (C) 2009, 2010 Google Inc. All rights reserved. + * + * This library 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 library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#ifndef TypeTraits_h +#define TypeTraits_h + +#include "Platform.h" + +#if (defined(__GLIBCXX__) && (__GLIBCXX__ >= 20070724) && defined(__GXX_EXPERIMENTAL_CXX0X__)) || (defined(_MSC_VER) && (_MSC_VER >= 1600)) +#include <type_traits> +#if defined(__GXX_EXPERIMENTAL_CXX0X__) +#include <tr1/memory> +#endif +#endif + +namespace WTF { + + // The following are provided in this file: + // + // IsInteger<T>::value + // IsPod<T>::value, see the definition for a note about its limitations + // IsConvertibleToInteger<T>::value + // + // IsSameType<T, U>::value + // + // RemovePointer<T>::Type + // RemoveReference<T>::Type + // RemoveConst<T>::Type + // RemoveVolatile<T>::Type + // RemoveConstVolatile<T>::Type + // + // COMPILE_ASSERT's in TypeTraits.cpp illustrate their usage and what they do. + + template<typename T> struct IsInteger { static const bool value = false; }; + template<> struct IsInteger<bool> { static const bool value = true; }; + template<> struct IsInteger<char> { static const bool value = true; }; + template<> struct IsInteger<signed char> { static const bool value = true; }; + template<> struct IsInteger<unsigned char> { static const bool value = true; }; + template<> struct IsInteger<short> { static const bool value = true; }; + template<> struct IsInteger<unsigned short> { static const bool value = true; }; + template<> struct IsInteger<int> { static const bool value = true; }; + template<> struct IsInteger<unsigned int> { static const bool value = true; }; + template<> struct IsInteger<long> { static const bool value = true; }; + template<> struct IsInteger<unsigned long> { static const bool value = true; }; + template<> struct IsInteger<long long> { static const bool value = true; }; + template<> struct IsInteger<unsigned long long> { static const bool value = true; }; +#if !COMPILER(MSVC) || defined(_NATIVE_WCHAR_T_DEFINED) + template<> struct IsInteger<wchar_t> { static const bool value = true; }; +#endif + + template<typename T> struct IsFloatingPoint { static const bool value = false; }; + template<> struct IsFloatingPoint<float> { static const bool value = true; }; + template<> struct IsFloatingPoint<double> { static const bool value = true; }; + template<> struct IsFloatingPoint<long double> { static const bool value = true; }; + + template<typename T> struct IsArithmetic { static const bool value = IsInteger<T>::value || IsFloatingPoint<T>::value; }; + + // IsPod is misnamed as it doesn't cover all plain old data (pod) types. + // Specifically, it doesn't allow for enums or for structs. + template <typename T> struct IsPod { static const bool value = IsArithmetic<T>::value; }; + template <typename P> struct IsPod<P*> { static const bool value = true; }; + + template<typename T> class IsConvertibleToInteger { + // Avoid "possible loss of data" warning when using Microsoft's C++ compiler + // by not converting int's to doubles. + template<bool performCheck, typename U> class IsConvertibleToDouble; + template<typename U> class IsConvertibleToDouble<false, U> { + public: + static const bool value = false; + }; + + template<typename U> class IsConvertibleToDouble<true, U> { + typedef char YesType; + struct NoType { + char padding[8]; + }; + + static YesType floatCheck(long double); + static NoType floatCheck(...); + static T& t; + public: + static const bool value = sizeof(floatCheck(t)) == sizeof(YesType); + }; + + public: + static const bool value = IsInteger<T>::value || IsConvertibleToDouble<!IsInteger<T>::value, T>::value; + }; + + template <typename T, typename U> struct IsSameType { + static const bool value = false; + }; + + template <typename T> struct IsSameType<T, T> { + static const bool value = true; + }; + + template <typename T, typename U> class IsSubclass { + typedef char YesType; + struct NoType { + char padding[8]; + }; + + static YesType subclassCheck(U*); + static NoType subclassCheck(...); + static T* t; + public: + static const bool value = sizeof(subclassCheck(t)) == sizeof(YesType); + }; + + template <typename T, template<class V> class U> class IsSubclassOfTemplate { + typedef char YesType; + struct NoType { + char padding[8]; + }; + + template<typename W> static YesType subclassCheck(U<W>*); + static NoType subclassCheck(...); + static T* t; + public: + static const bool value = sizeof(subclassCheck(t)) == sizeof(YesType); + }; + + template <typename T, template <class V> class OuterTemplate> struct RemoveTemplate { + typedef T Type; + }; + + template <typename T, template <class V> class OuterTemplate> struct RemoveTemplate<OuterTemplate<T>, OuterTemplate> { + typedef T Type; + }; + + template <typename T> struct RemoveConst { + typedef T Type; + }; + + template <typename T> struct RemoveConst<const T> { + typedef T Type; + }; + + template <typename T> struct RemoveVolatile { + typedef T Type; + }; + + template <typename T> struct RemoveVolatile<volatile T> { + typedef T Type; + }; + + template <typename T> struct RemoveConstVolatile { + typedef typename RemoveVolatile<typename RemoveConst<T>::Type>::Type Type; + }; + + template <typename T> struct RemovePointer { + typedef T Type; + }; + + template <typename T> struct RemovePointer<T*> { + typedef T Type; + }; + + template <typename T> struct RemoveReference { + typedef T Type; + }; + + template <typename T> struct RemoveReference<T&> { + typedef T Type; + }; + +#if (defined(__GLIBCXX__) && (__GLIBCXX__ >= 20070724) && defined(__GXX_EXPERIMENTAL_CXX0X__)) || (defined(_MSC_VER) && (_MSC_VER >= 1600)) + + // GCC's libstdc++ 20070724 and later supports C++ TR1 type_traits in the std namespace. + // VC10 (VS2010) and later support C++ TR1 type_traits in the std::tr1 namespace. + template<typename T> struct HasTrivialConstructor : public std::tr1::has_trivial_constructor<T> { }; + template<typename T> struct HasTrivialDestructor : public std::tr1::has_trivial_destructor<T> { }; + +#else + + // This compiler doesn't provide type traits, so we provide basic HasTrivialConstructor + // and HasTrivialDestructor definitions. The definitions here include most built-in + // scalar types but do not include POD structs and classes. For the intended purposes of + // type_traits this results correct but potentially less efficient code. + template <typename T, T v> + struct IntegralConstant { + static const T value = v; + typedef T value_type; + typedef IntegralConstant<T, v> type; + }; + + typedef IntegralConstant<bool, true> true_type; + typedef IntegralConstant<bool, false> false_type; + +#if defined(_MSC_VER) && (_MSC_VER >= 1400) && !defined(__INTEL_COMPILER) + // VC8 (VS2005) and later have built-in compiler support for HasTrivialConstructor / HasTrivialDestructor, + // but for some unexplained reason it doesn't work on built-in types. + template <typename T> struct HasTrivialConstructor : public IntegralConstant<bool, __has_trivial_constructor(T)>{ }; + template <typename T> struct HasTrivialDestructor : public IntegralConstant<bool, __has_trivial_destructor(T)>{ }; +#else + template <typename T> struct HasTrivialConstructor : public false_type{ }; + template <typename T> struct HasTrivialDestructor : public false_type{ }; +#endif + + template <typename T> struct HasTrivialConstructor<T*> : public true_type{ }; + template <typename T> struct HasTrivialDestructor<T*> : public true_type{ }; + + template <> struct HasTrivialConstructor<float> : public true_type{ }; + template <> struct HasTrivialConstructor<const float> : public true_type{ }; + template <> struct HasTrivialConstructor<volatile float> : public true_type{ }; + template <> struct HasTrivialConstructor<const volatile float> : public true_type{ }; + + template <> struct HasTrivialConstructor<double> : public true_type{ }; + template <> struct HasTrivialConstructor<const double> : public true_type{ }; + template <> struct HasTrivialConstructor<volatile double> : public true_type{ }; + template <> struct HasTrivialConstructor<const volatile double> : public true_type{ }; + + template <> struct HasTrivialConstructor<long double> : public true_type{ }; + template <> struct HasTrivialConstructor<const long double> : public true_type{ }; + template <> struct HasTrivialConstructor<volatile long double> : public true_type{ }; + template <> struct HasTrivialConstructor<const volatile long double> : public true_type{ }; + + template <> struct HasTrivialConstructor<unsigned char> : public true_type{ }; + template <> struct HasTrivialConstructor<const unsigned char> : public true_type{ }; + template <> struct HasTrivialConstructor<volatile unsigned char> : public true_type{ }; + template <> struct HasTrivialConstructor<const volatile unsigned char> : public true_type{ }; + + template <> struct HasTrivialConstructor<unsigned short> : public true_type{ }; + template <> struct HasTrivialConstructor<const unsigned short> : public true_type{ }; + template <> struct HasTrivialConstructor<volatile unsigned short> : public true_type{ }; + template <> struct HasTrivialConstructor<const volatile unsigned short> : public true_type{ }; + + template <> struct HasTrivialConstructor<unsigned int> : public true_type{ }; + template <> struct HasTrivialConstructor<const unsigned int> : public true_type{ }; + template <> struct HasTrivialConstructor<volatile unsigned int> : public true_type{ }; + template <> struct HasTrivialConstructor<const volatile unsigned int> : public true_type{ }; + + template <> struct HasTrivialConstructor<unsigned long> : public true_type{ }; + template <> struct HasTrivialConstructor<const unsigned long> : public true_type{ }; + template <> struct HasTrivialConstructor<volatile unsigned long> : public true_type{ }; + template <> struct HasTrivialConstructor<const volatile unsigned long> : public true_type{ }; + + template <> struct HasTrivialConstructor<unsigned long long> : public true_type{ }; + template <> struct HasTrivialConstructor<const unsigned long long> : public true_type{ }; + template <> struct HasTrivialConstructor<volatile unsigned long long> : public true_type{ }; + template <> struct HasTrivialConstructor<const volatile unsigned long long> : public true_type{ }; + + template <> struct HasTrivialConstructor<signed char> : public true_type{ }; + template <> struct HasTrivialConstructor<const signed char> : public true_type{ }; + template <> struct HasTrivialConstructor<volatile signed char> : public true_type{ }; + template <> struct HasTrivialConstructor<const volatile signed char> : public true_type{ }; + + template <> struct HasTrivialConstructor<signed short> : public true_type{ }; + template <> struct HasTrivialConstructor<const signed short> : public true_type{ }; + template <> struct HasTrivialConstructor<volatile signed short> : public true_type{ }; + template <> struct HasTrivialConstructor<const volatile signed short> : public true_type{ }; + + template <> struct HasTrivialConstructor<signed int> : public true_type{ }; + template <> struct HasTrivialConstructor<const signed int> : public true_type{ }; + template <> struct HasTrivialConstructor<volatile signed int> : public true_type{ }; + template <> struct HasTrivialConstructor<const volatile signed int> : public true_type{ }; + + template <> struct HasTrivialConstructor<signed long> : public true_type{ }; + template <> struct HasTrivialConstructor<const signed long> : public true_type{ }; + template <> struct HasTrivialConstructor<volatile signed long> : public true_type{ }; + template <> struct HasTrivialConstructor<const volatile signed long> : public true_type{ }; + + template <> struct HasTrivialConstructor<signed long long> : public true_type{ }; + template <> struct HasTrivialConstructor<const signed long long> : public true_type{ }; + template <> struct HasTrivialConstructor<volatile signed long long> : public true_type{ }; + template <> struct HasTrivialConstructor<const volatile signed long long> : public true_type{ }; + + template <> struct HasTrivialConstructor<bool> : public true_type{ }; + template <> struct HasTrivialConstructor<const bool> : public true_type{ }; + template <> struct HasTrivialConstructor<volatile bool> : public true_type{ }; + template <> struct HasTrivialConstructor<const volatile bool> : public true_type{ }; + + template <> struct HasTrivialConstructor<char> : public true_type{ }; + template <> struct HasTrivialConstructor<const char> : public true_type{ }; + template <> struct HasTrivialConstructor<volatile char> : public true_type{ }; + template <> struct HasTrivialConstructor<const volatile char> : public true_type{ }; + + #if !defined(_MSC_VER) || defined(_NATIVE_WCHAR_T_DEFINED) + template <> struct HasTrivialConstructor<wchar_t> : public true_type{ }; + template <> struct HasTrivialConstructor<const wchar_t> : public true_type{ }; + template <> struct HasTrivialConstructor<volatile wchar_t> : public true_type{ }; + template <> struct HasTrivialConstructor<const volatile wchar_t> : public true_type{ }; + #endif + + template <> struct HasTrivialDestructor<float> : public true_type{ }; + template <> struct HasTrivialDestructor<const float> : public true_type{ }; + template <> struct HasTrivialDestructor<volatile float> : public true_type{ }; + template <> struct HasTrivialDestructor<const volatile float> : public true_type{ }; + + template <> struct HasTrivialDestructor<double> : public true_type{ }; + template <> struct HasTrivialDestructor<const double> : public true_type{ }; + template <> struct HasTrivialDestructor<volatile double> : public true_type{ }; + template <> struct HasTrivialDestructor<const volatile double> : public true_type{ }; + + template <> struct HasTrivialDestructor<long double> : public true_type{ }; + template <> struct HasTrivialDestructor<const long double> : public true_type{ }; + template <> struct HasTrivialDestructor<volatile long double> : public true_type{ }; + template <> struct HasTrivialDestructor<const volatile long double> : public true_type{ }; + + template <> struct HasTrivialDestructor<unsigned char> : public true_type{ }; + template <> struct HasTrivialDestructor<const unsigned char> : public true_type{ }; + template <> struct HasTrivialDestructor<volatile unsigned char> : public true_type{ }; + template <> struct HasTrivialDestructor<const volatile unsigned char> : public true_type{ }; + + template <> struct HasTrivialDestructor<unsigned short> : public true_type{ }; + template <> struct HasTrivialDestructor<const unsigned short> : public true_type{ }; + template <> struct HasTrivialDestructor<volatile unsigned short> : public true_type{ }; + template <> struct HasTrivialDestructor<const volatile unsigned short> : public true_type{ }; + + template <> struct HasTrivialDestructor<unsigned int> : public true_type{ }; + template <> struct HasTrivialDestructor<const unsigned int> : public true_type{ }; + template <> struct HasTrivialDestructor<volatile unsigned int> : public true_type{ }; + template <> struct HasTrivialDestructor<const volatile unsigned int> : public true_type{ }; + + template <> struct HasTrivialDestructor<unsigned long> : public true_type{ }; + template <> struct HasTrivialDestructor<const unsigned long> : public true_type{ }; + template <> struct HasTrivialDestructor<volatile unsigned long> : public true_type{ }; + template <> struct HasTrivialDestructor<const volatile unsigned long> : public true_type{ }; + + template <> struct HasTrivialDestructor<unsigned long long> : public true_type{ }; + template <> struct HasTrivialDestructor<const unsigned long long> : public true_type{ }; + template <> struct HasTrivialDestructor<volatile unsigned long long> : public true_type{ }; + template <> struct HasTrivialDestructor<const volatile unsigned long long> : public true_type{ }; + + template <> struct HasTrivialDestructor<signed char> : public true_type{ }; + template <> struct HasTrivialDestructor<const signed char> : public true_type{ }; + template <> struct HasTrivialDestructor<volatile signed char> : public true_type{ }; + template <> struct HasTrivialDestructor<const volatile signed char> : public true_type{ }; + + template <> struct HasTrivialDestructor<signed short> : public true_type{ }; + template <> struct HasTrivialDestructor<const signed short> : public true_type{ }; + template <> struct HasTrivialDestructor<volatile signed short> : public true_type{ }; + template <> struct HasTrivialDestructor<const volatile signed short> : public true_type{ }; + + template <> struct HasTrivialDestructor<signed int> : public true_type{ }; + template <> struct HasTrivialDestructor<const signed int> : public true_type{ }; + template <> struct HasTrivialDestructor<volatile signed int> : public true_type{ }; + template <> struct HasTrivialDestructor<const volatile signed int> : public true_type{ }; + + template <> struct HasTrivialDestructor<signed long> : public true_type{ }; + template <> struct HasTrivialDestructor<const signed long> : public true_type{ }; + template <> struct HasTrivialDestructor<volatile signed long> : public true_type{ }; + template <> struct HasTrivialDestructor<const volatile signed long> : public true_type{ }; + + template <> struct HasTrivialDestructor<signed long long> : public true_type{ }; + template <> struct HasTrivialDestructor<const signed long long> : public true_type{ }; + template <> struct HasTrivialDestructor<volatile signed long long> : public true_type{ }; + template <> struct HasTrivialDestructor<const volatile signed long long> : public true_type{ }; + + template <> struct HasTrivialDestructor<bool> : public true_type{ }; + template <> struct HasTrivialDestructor<const bool> : public true_type{ }; + template <> struct HasTrivialDestructor<volatile bool> : public true_type{ }; + template <> struct HasTrivialDestructor<const volatile bool> : public true_type{ }; + + template <> struct HasTrivialDestructor<char> : public true_type{ }; + template <> struct HasTrivialDestructor<const char> : public true_type{ }; + template <> struct HasTrivialDestructor<volatile char> : public true_type{ }; + template <> struct HasTrivialDestructor<const volatile char> : public true_type{ }; + + #if !defined(_MSC_VER) || defined(_NATIVE_WCHAR_T_DEFINED) + template <> struct HasTrivialDestructor<wchar_t> : public true_type{ }; + template <> struct HasTrivialDestructor<const wchar_t> : public true_type{ }; + template <> struct HasTrivialDestructor<volatile wchar_t> : public true_type{ }; + template <> struct HasTrivialDestructor<const volatile wchar_t> : public true_type{ }; + #endif + +#endif // __GLIBCXX__, etc. + +} // namespace WTF + +#endif // TypeTraits_h diff --git a/Source/JavaScriptCore/wtf/TypedArrayBase.h b/Source/JavaScriptCore/wtf/TypedArrayBase.h new file mode 100644 index 000000000..dba95d55f --- /dev/null +++ b/Source/JavaScriptCore/wtf/TypedArrayBase.h @@ -0,0 +1,130 @@ +/* + * Copyright (C) 2010 Apple Inc. All rights reserved. + * Copyright (c) 2010, Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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 APPLE COMPUTER, INC. ``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 APPLE COMPUTER, INC. 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. + */ + +#ifndef TypedArrayBase_h +#define TypedArrayBase_h + +#include "ArrayBuffer.h" +#include "ArrayBufferView.h" + +namespace WTF { + +template <typename T> +class TypedArrayBase : public ArrayBufferView { + public: + T* data() const { return static_cast<T*>(baseAddress()); } + + bool set(TypedArrayBase<T>* array, unsigned offset) + { + return setImpl(array, offset * sizeof(T)); + } + + bool setRange(const T* data, size_t dataLength, unsigned offset) + { + return setRangeImpl(reinterpret_cast<const char*>(data), dataLength * sizeof(T), offset * sizeof(T)); + } + + bool zeroRange(unsigned offset, size_t length) + { + return zeroRangeImpl(offset * sizeof(T), length * sizeof(T)); + } + + // Overridden from ArrayBufferView. This must be public because of + // rules about inheritance of members in template classes, and + // because it is accessed via pointers to subclasses. + unsigned length() const + { + return m_length; + } + + virtual unsigned byteLength() const + { + return m_length * sizeof(T); + } + +protected: + TypedArrayBase(PassRefPtr<ArrayBuffer> buffer, unsigned byteOffset, unsigned length) + : ArrayBufferView(buffer, byteOffset) + , m_length(length) + { + } + + template <class Subclass> + static PassRefPtr<Subclass> create(unsigned length) + { + RefPtr<ArrayBuffer> buffer = ArrayBuffer::create(length, sizeof(T)); + if (!buffer.get()) + return 0; + return create<Subclass>(buffer, 0, length); + } + + template <class Subclass> + static PassRefPtr<Subclass> create(const T* array, unsigned length) + { + RefPtr<Subclass> a = create<Subclass>(length); + if (a) + for (unsigned i = 0; i < length; ++i) + a->set(i, array[i]); + return a; + } + + template <class Subclass> + static PassRefPtr<Subclass> create(PassRefPtr<ArrayBuffer> buffer, + unsigned byteOffset, + unsigned length) + { + RefPtr<ArrayBuffer> buf(buffer); + if (!verifySubRange<T>(buf, byteOffset, length)) + return 0; + + return adoptRef(new Subclass(buf, byteOffset, length)); + } + + template <class Subclass> + PassRefPtr<Subclass> subarrayImpl(int start, int end) const + { + unsigned offset, length; + calculateOffsetAndLength(start, end, m_length, &offset, &length); + clampOffsetAndNumElements<T>(buffer(), m_byteOffset, &offset, &length); + return create<Subclass>(buffer(), offset, length); + } + + virtual void neuter() + { + ArrayBufferView::neuter(); + m_length = 0; + } + + // We do not want to have to access this via a virtual function in subclasses, + // which is why it is protected rather than private. + unsigned m_length; +}; + +} // namespace WTF + +using WTF::TypedArrayBase; + +#endif // TypedArrayBase_h diff --git a/Source/JavaScriptCore/wtf/Uint16Array.h b/Source/JavaScriptCore/wtf/Uint16Array.h new file mode 100644 index 000000000..2d0765051 --- /dev/null +++ b/Source/JavaScriptCore/wtf/Uint16Array.h @@ -0,0 +1,94 @@ +/* + * Copyright (C) 2009 Apple Inc. All rights reserved. + * Copyright (C) 2009 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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 APPLE COMPUTER, INC. ``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 APPLE COMPUTER, INC. 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. + */ + +#ifndef Uint16Array_h +#define Uint16Array_h + +#include "IntegralTypedArrayBase.h" + +namespace WTF { + +class ArrayBuffer; + +class Uint16Array : public IntegralTypedArrayBase<unsigned short> { +public: + static inline PassRefPtr<Uint16Array> create(unsigned length); + static inline PassRefPtr<Uint16Array> create(unsigned short* array, unsigned length); + static inline PassRefPtr<Uint16Array> create(PassRefPtr<ArrayBuffer>, unsigned byteOffset, unsigned length); + + // Can’t use "using" here due to a bug in the RVCT compiler. + bool set(TypedArrayBase<unsigned short>* array, unsigned offset) { return TypedArrayBase<unsigned short>::set(array, offset); } + void set(unsigned index, double value) { IntegralTypedArrayBase<unsigned short>::set(index, value); } + + inline PassRefPtr<Uint16Array> subarray(int start) const; + inline PassRefPtr<Uint16Array> subarray(int start, int end) const; + +private: + inline Uint16Array(PassRefPtr<ArrayBuffer>, + unsigned byteOffset, + unsigned length); + // Make constructor visible to superclass. + friend class TypedArrayBase<unsigned short>; + + // Overridden from ArrayBufferView. + virtual bool isUnsignedShortArray() const { return true; } +}; + +PassRefPtr<Uint16Array> Uint16Array::create(unsigned length) +{ + return TypedArrayBase<unsigned short>::create<Uint16Array>(length); +} + +PassRefPtr<Uint16Array> Uint16Array::create(unsigned short* array, unsigned length) +{ + return TypedArrayBase<unsigned short>::create<Uint16Array>(array, length); +} + +PassRefPtr<Uint16Array> Uint16Array::create(PassRefPtr<ArrayBuffer> buffer, unsigned byteOffset, unsigned length) +{ + return TypedArrayBase<unsigned short>::create<Uint16Array>(buffer, byteOffset, length); +} + +Uint16Array::Uint16Array(PassRefPtr<ArrayBuffer> buffer, unsigned byteOffset, unsigned length) + : IntegralTypedArrayBase<unsigned short>(buffer, byteOffset, length) +{ +} + +PassRefPtr<Uint16Array> Uint16Array::subarray(int start) const +{ + return subarray(start, length()); +} + +PassRefPtr<Uint16Array> Uint16Array::subarray(int start, int end) const +{ + return subarrayImpl<Uint16Array>(start, end); +} + +} // namespace WTF + +using WTF::Uint16Array; + +#endif // Uint16Array_h diff --git a/Source/JavaScriptCore/wtf/Uint32Array.h b/Source/JavaScriptCore/wtf/Uint32Array.h new file mode 100644 index 000000000..3765f8077 --- /dev/null +++ b/Source/JavaScriptCore/wtf/Uint32Array.h @@ -0,0 +1,94 @@ +/* + * Copyright (C) 2009 Apple Inc. All rights reserved. + * Copyright (C) 2009 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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 APPLE COMPUTER, INC. ``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 APPLE COMPUTER, INC. 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. + */ + +#ifndef Uint32Array_h +#define Uint32Array_h + +#include "IntegralTypedArrayBase.h" + +namespace WTF { + +class ArrayBuffer; + +class Uint32Array : public IntegralTypedArrayBase<unsigned int> { +public: + static inline PassRefPtr<Uint32Array> create(unsigned length); + static inline PassRefPtr<Uint32Array> create(unsigned int* array, unsigned length); + static inline PassRefPtr<Uint32Array> create(PassRefPtr<ArrayBuffer>, unsigned byteOffset, unsigned length); + + // Can’t use "using" here due to a bug in the RVCT compiler. + bool set(TypedArrayBase<unsigned int>* array, unsigned offset) { return TypedArrayBase<unsigned int>::set(array, offset); } + void set(unsigned index, double value) { IntegralTypedArrayBase<unsigned int>::set(index, value); } + + inline PassRefPtr<Uint32Array> subarray(int start) const; + inline PassRefPtr<Uint32Array> subarray(int start, int end) const; + +private: + inline Uint32Array(PassRefPtr<ArrayBuffer>, + unsigned byteOffset, + unsigned length); + // Make constructor visible to superclass. + friend class TypedArrayBase<unsigned int>; + + // Overridden from ArrayBufferView. + virtual bool isUnsignedIntArray() const { return true; } +}; + +PassRefPtr<Uint32Array> Uint32Array::create(unsigned length) +{ + return TypedArrayBase<unsigned int>::create<Uint32Array>(length); +} + +PassRefPtr<Uint32Array> Uint32Array::create(unsigned int* array, unsigned length) +{ + return TypedArrayBase<unsigned int>::create<Uint32Array>(array, length); +} + +PassRefPtr<Uint32Array> Uint32Array::create(PassRefPtr<ArrayBuffer> buffer, unsigned byteOffset, unsigned length) +{ + return TypedArrayBase<unsigned int>::create<Uint32Array>(buffer, byteOffset, length); +} + +Uint32Array::Uint32Array(PassRefPtr<ArrayBuffer> buffer, unsigned byteOffset, unsigned length) + : IntegralTypedArrayBase<unsigned int>(buffer, byteOffset, length) +{ +} + +PassRefPtr<Uint32Array> Uint32Array::subarray(int start) const +{ + return subarray(start, length()); +} + +PassRefPtr<Uint32Array> Uint32Array::subarray(int start, int end) const +{ + return subarrayImpl<Uint32Array>(start, end); +} + +} // namespace WTF + +using WTF::Uint32Array; + +#endif // Uint32Array_h diff --git a/Source/JavaScriptCore/wtf/Uint8Array.h b/Source/JavaScriptCore/wtf/Uint8Array.h new file mode 100644 index 000000000..58fdb842b --- /dev/null +++ b/Source/JavaScriptCore/wtf/Uint8Array.h @@ -0,0 +1,94 @@ +/* + * Copyright (C) 2009 Apple Inc. All rights reserved. + * Copyright (C) 2009 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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 APPLE COMPUTER, INC. ``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 APPLE COMPUTER, INC. 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. + */ + +#ifndef Uint8Array_h +#define Uint8Array_h + +#include "IntegralTypedArrayBase.h" + +namespace WTF { + +class ArrayBuffer; + +class Uint8Array : public IntegralTypedArrayBase<unsigned char> { +public: + static inline PassRefPtr<Uint8Array> create(unsigned length); + static inline PassRefPtr<Uint8Array> create(unsigned char* array, unsigned length); + static inline PassRefPtr<Uint8Array> create(PassRefPtr<ArrayBuffer>, unsigned byteOffset, unsigned length); + + // Can’t use "using" here due to a bug in the RVCT compiler. + bool set(TypedArrayBase<unsigned char>* array, unsigned offset) { return TypedArrayBase<unsigned char>::set(array, offset); } + void set(unsigned index, double value) { IntegralTypedArrayBase<unsigned char>::set(index, value); } + + inline PassRefPtr<Uint8Array> subarray(int start) const; + inline PassRefPtr<Uint8Array> subarray(int start, int end) const; + +private: + inline Uint8Array(PassRefPtr<ArrayBuffer>, + unsigned byteOffset, + unsigned length); + // Make constructor visible to superclass. + friend class TypedArrayBase<unsigned char>; + + // Overridden from ArrayBufferView. + virtual bool isUnsignedByteArray() const { return true; } +}; + +PassRefPtr<Uint8Array> Uint8Array::create(unsigned length) +{ + return TypedArrayBase<unsigned char>::create<Uint8Array>(length); +} + +PassRefPtr<Uint8Array> Uint8Array::create(unsigned char* array, unsigned length) +{ + return TypedArrayBase<unsigned char>::create<Uint8Array>(array, length); +} + +PassRefPtr<Uint8Array> Uint8Array::create(PassRefPtr<ArrayBuffer> buffer, unsigned byteOffset, unsigned length) +{ + return TypedArrayBase<unsigned char>::create<Uint8Array>(buffer, byteOffset, length); +} + +Uint8Array::Uint8Array(PassRefPtr<ArrayBuffer> buffer, unsigned byteOffset, unsigned length) +: IntegralTypedArrayBase<unsigned char>(buffer, byteOffset, length) +{ +} + +PassRefPtr<Uint8Array> Uint8Array::subarray(int start) const +{ + return subarray(start, length()); +} + +PassRefPtr<Uint8Array> Uint8Array::subarray(int start, int end) const +{ + return subarrayImpl<Uint8Array>(start, end); +} + +} // namespace WTF + +using WTF::Uint8Array; + +#endif // Uint8Array_h diff --git a/Source/JavaScriptCore/wtf/UnionFind.h b/Source/JavaScriptCore/wtf/UnionFind.h new file mode 100644 index 000000000..fa737116c --- /dev/null +++ b/Source/JavaScriptCore/wtf/UnionFind.h @@ -0,0 +1,105 @@ +/* + * Copyright (C) 2011 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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 APPLE INC. ``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 APPLE INC. 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. + */ + +#ifndef UnionFind_h +#define UnionFind_h + +#include <wtf/Assertions.h> + +namespace WTF { + +// A UnionFind class can be used to compute disjoint sets using the +// disjoint-set forest data structure. Each UnionFind instance is a +// node in the forest. Typically you use it by using UnionFind as a +// superclass: +// +// class MemberOfSet : public UnionFind<MemberOfSet> { ... } +// +// Calling x->find() gives you a MemberOfSet* that represents the +// disjoint set that x belongs to. Calling x->unify(y) unifies x's +// set with y's set, and ensures that: +// +// x->find() == y->find() +// +// and that: +// +// a->find() == b->find() +// +// for any a, b if prior to the call to x->unify(y), we would have +// had: +// +// a->find() == x +// b->find() == y +// +// This implementation is almost amortized O(1), but could be worse +// in unlikely pathological cases. It favors having a non-recursive +// single pass implementation of unify() and find() over ensuring the +// theoretical O(InverseAckermann[n]) amortized bound, which is much +// closer to amortized O(1). + +template<typename T> +class UnionFind { +public: + UnionFind() + : m_parent(0) + { + } + + T* find() + { + T* result = static_cast<T*>(this); + T* next = result->m_parent; + while (next) { + result = next; + next = result->m_parent; + } + ASSERT(result); + if (result != this) + m_parent = result; + return result; + } + + void unify(T* other) + { + T* a = static_cast<T*>(this)->find(); + T* b = other->find(); + + ASSERT(!a->m_parent); + ASSERT(!b->m_parent); + + if (a == b) + return; + + a->m_parent = b; + } +private: + T* m_parent; +}; + +} // namespace WTF + +using WTF::UnionFind; + +#endif // UnionFind_h diff --git a/Source/JavaScriptCore/wtf/UnusedParam.h b/Source/JavaScriptCore/wtf/UnusedParam.h new file mode 100644 index 000000000..6ff6fd895 --- /dev/null +++ b/Source/JavaScriptCore/wtf/UnusedParam.h @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2006 Apple Computer, Inc. + * + * This library 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 library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#ifndef WTF_UnusedParam_h +#define WTF_UnusedParam_h + +/* don't use this for C++, it should only be used in plain C files or + ObjC methods, where leaving off the parameter name is not allowed. */ + +#include "Platform.h" + +#if COMPILER(INTEL) && !OS(WINDOWS) || COMPILER(RVCT) +template<typename T> +inline void unusedParam(T& x) { (void)x; } +#define UNUSED_PARAM(variable) unusedParam(variable) +#else +#define UNUSED_PARAM(variable) (void)variable +#endif + +#endif /* WTF_UnusedParam_h */ diff --git a/Source/JavaScriptCore/wtf/VMTags.h b/Source/JavaScriptCore/wtf/VMTags.h new file mode 100644 index 000000000..117bc3721 --- /dev/null +++ b/Source/JavaScriptCore/wtf/VMTags.h @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2009 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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 APPLE INC. ``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 APPLE INC. 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. + */ + +#ifndef VMTags_h +#define VMTags_h + +// On Mac OS X, the VM subsystem allows tagging memory requested from mmap and vm_map +// in order to aid tools that inspect system memory use. +#if OS(DARWIN) + +#include <mach/vm_statistics.h> + +#if defined(VM_MEMORY_TCMALLOC) +#define VM_TAG_FOR_TCMALLOC_MEMORY VM_MAKE_TAG(VM_MEMORY_TCMALLOC) +#else +#define VM_TAG_FOR_TCMALLOC_MEMORY VM_MAKE_TAG(53) +#endif // defined(VM_MEMORY_TCMALLOC) + +#if defined(VM_MEMORY_JAVASCRIPT_JIT_EXECUTABLE_ALLOCATOR) +#define VM_TAG_FOR_EXECUTABLEALLOCATOR_MEMORY VM_MAKE_TAG(VM_MEMORY_JAVASCRIPT_JIT_EXECUTABLE_ALLOCATOR) +#else +#define VM_TAG_FOR_EXECUTABLEALLOCATOR_MEMORY VM_MAKE_TAG(64) +#endif // defined(VM_MEMORY_JAVASCRIPT_JIT_EXECUTABLE_ALLOCATOR) + +#if defined(VM_MEMORY_JAVASCRIPT_JIT_REGISTER_FILE) +#define VM_TAG_FOR_REGISTERFILE_MEMORY VM_MAKE_TAG(VM_MEMORY_JAVASCRIPT_JIT_REGISTER_FILE) +#else +#define VM_TAG_FOR_REGISTERFILE_MEMORY VM_MAKE_TAG(65) +#endif // defined(VM_MEMORY_JAVASCRIPT_JIT_REGISTER_FILE) + +#if defined(VM_MEMORY_JAVASCRIPT_CORE) +#define VM_TAG_FOR_COLLECTOR_MEMORY VM_MAKE_TAG(VM_MEMORY_JAVASCRIPT_CORE) +#else +#define VM_TAG_FOR_COLLECTOR_MEMORY VM_MAKE_TAG(63) +#endif // defined(VM_MEMORY_JAVASCRIPT_CORE) + +#if defined(VM_MEMORY_WEBCORE_PURGEABLE_BUFFERS) +#define VM_TAG_FOR_WEBCORE_PURGEABLE_MEMORY VM_MAKE_TAG(VM_MEMORY_WEBCORE_PURGEABLE_BUFFERS) +#else +#define VM_TAG_FOR_WEBCORE_PURGEABLE_MEMORY VM_MAKE_TAG(69) +#endif // defined(VM_MEMORY_WEBCORE_PURGEABLE_BUFFERS) + +#else // OS(DARWIN) + +#define VM_TAG_FOR_TCMALLOC_MEMORY -1 +#define VM_TAG_FOR_COLLECTOR_MEMORY -1 +#define VM_TAG_FOR_EXECUTABLEALLOCATOR_MEMORY -1 +#define VM_TAG_FOR_REGISTERFILE_MEMORY -1 +#define VM_TAG_FOR_WEBCORE_PURGEABLE_MEMORY -1 + +#endif // OS(DARWIN) + +#endif // VMTags_h diff --git a/Source/JavaScriptCore/wtf/ValueCheck.h b/Source/JavaScriptCore/wtf/ValueCheck.h new file mode 100644 index 000000000..2a86eb0f2 --- /dev/null +++ b/Source/JavaScriptCore/wtf/ValueCheck.h @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2010 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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 APPLE INC. ``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 APPLE INC. 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. + */ + +#ifndef ValueCheck_h +#define ValueCheck_h + +#include <wtf/FastMalloc.h> + +namespace WTF { + +template<typename T> struct ValueCheck { + typedef T TraitType; + static void checkConsistency(const T&) { } +}; + +#if !ASSERT_DISABLED +template<typename P> struct ValueCheck<P*> { + typedef P* TraitType; + static void checkConsistency(const P* p) + { + if (!p) + return; + ASSERT(fastMallocSize(p)); + ValueCheck<P>::checkConsistency(*p); + } +}; +#endif + +} + +#endif // ValueCheck_h diff --git a/Source/JavaScriptCore/wtf/Vector.h b/Source/JavaScriptCore/wtf/Vector.h new file mode 100644 index 000000000..1368ac99b --- /dev/null +++ b/Source/JavaScriptCore/wtf/Vector.h @@ -0,0 +1,1191 @@ +/* + * Copyright (C) 2005, 2006, 2007, 2008 Apple Inc. All rights reserved. + * + * This library 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 library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#ifndef WTF_Vector_h +#define WTF_Vector_h + +#include "Alignment.h" +#include "FastAllocBase.h" +#include "Noncopyable.h" +#include "NotFound.h" +#include "StdLibExtras.h" +#include "ValueCheck.h" +#include "VectorTraits.h" +#include <limits> +#include <utility> + +#if PLATFORM(QT) +#include <QDataStream> +#endif + +namespace WTF { + + using std::min; + using std::max; + + template <bool needsDestruction, typename T> + struct VectorDestructor; + + template<typename T> + struct VectorDestructor<false, T> + { + static void destruct(T*, T*) {} + }; + + template<typename T> + struct VectorDestructor<true, T> + { + static void destruct(T* begin, T* end) + { + for (T* cur = begin; cur != end; ++cur) + cur->~T(); + } + }; + + template <bool needsInitialization, bool canInitializeWithMemset, typename T> + struct VectorInitializer; + + template<bool ignore, typename T> + struct VectorInitializer<false, ignore, T> + { + static void initialize(T*, T*) {} + }; + + template<typename T> + struct VectorInitializer<true, false, T> + { + static void initialize(T* begin, T* end) + { + for (T* cur = begin; cur != end; ++cur) + new (NotNull, cur) T; + } + }; + + template<typename T> + struct VectorInitializer<true, true, T> + { + static void initialize(T* begin, T* end) + { + memset(begin, 0, reinterpret_cast<char*>(end) - reinterpret_cast<char*>(begin)); + } + }; + + template <bool canMoveWithMemcpy, typename T> + struct VectorMover; + + template<typename T> + struct VectorMover<false, T> + { + static void move(const T* src, const T* srcEnd, T* dst) + { + while (src != srcEnd) { + new (NotNull, dst) T(*src); +#if COMPILER(SUNCC) && __SUNPRO_CC <= 0x590 + const_cast<T*>(src)->~T(); // Work around obscure SunCC 12 compiler bug. +#else + src->~T(); +#endif + ++dst; + ++src; + } + } + static void moveOverlapping(const T* src, const T* srcEnd, T* dst) + { + if (src > dst) + move(src, srcEnd, dst); + else { + T* dstEnd = dst + (srcEnd - src); + while (src != srcEnd) { + --srcEnd; + --dstEnd; + new (NotNull, dstEnd) T(*srcEnd); + srcEnd->~T(); + } + } + } + }; + + template<typename T> + struct VectorMover<true, T> + { + static void move(const T* src, const T* srcEnd, T* dst) + { + memcpy(dst, src, reinterpret_cast<const char*>(srcEnd) - reinterpret_cast<const char*>(src)); + } + static void moveOverlapping(const T* src, const T* srcEnd, T* dst) + { + memmove(dst, src, reinterpret_cast<const char*>(srcEnd) - reinterpret_cast<const char*>(src)); + } + }; + + template <bool canCopyWithMemcpy, typename T> + struct VectorCopier; + + template<typename T> + struct VectorCopier<false, T> + { + static void uninitializedCopy(const T* src, const T* srcEnd, T* dst) + { + while (src != srcEnd) { + new (NotNull, dst) T(*src); + ++dst; + ++src; + } + } + }; + + template<typename T> + struct VectorCopier<true, T> + { + static void uninitializedCopy(const T* src, const T* srcEnd, T* dst) + { + memcpy(dst, src, reinterpret_cast<const char*>(srcEnd) - reinterpret_cast<const char*>(src)); + } + }; + + template <bool canFillWithMemset, typename T> + struct VectorFiller; + + template<typename T> + struct VectorFiller<false, T> + { + static void uninitializedFill(T* dst, T* dstEnd, const T& val) + { + while (dst != dstEnd) { + new (NotNull, dst) T(val); + ++dst; + } + } + }; + + template<typename T> + struct VectorFiller<true, T> + { + static void uninitializedFill(T* dst, T* dstEnd, const T& val) + { + ASSERT(sizeof(T) == sizeof(char)); + memset(dst, val, dstEnd - dst); + } + }; + + template<bool canCompareWithMemcmp, typename T> + struct VectorComparer; + + template<typename T> + struct VectorComparer<false, T> + { + static bool compare(const T* a, const T* b, size_t size) + { + for (size_t i = 0; i < size; ++i) + if (a[i] != b[i]) + return false; + return true; + } + }; + + template<typename T> + struct VectorComparer<true, T> + { + static bool compare(const T* a, const T* b, size_t size) + { + return memcmp(a, b, sizeof(T) * size) == 0; + } + }; + + template<typename T> + struct VectorTypeOperations + { + static void destruct(T* begin, T* end) + { + VectorDestructor<VectorTraits<T>::needsDestruction, T>::destruct(begin, end); + } + + static void initialize(T* begin, T* end) + { + VectorInitializer<VectorTraits<T>::needsInitialization, VectorTraits<T>::canInitializeWithMemset, T>::initialize(begin, end); + } + + static void move(const T* src, const T* srcEnd, T* dst) + { + VectorMover<VectorTraits<T>::canMoveWithMemcpy, T>::move(src, srcEnd, dst); + } + + static void moveOverlapping(const T* src, const T* srcEnd, T* dst) + { + VectorMover<VectorTraits<T>::canMoveWithMemcpy, T>::moveOverlapping(src, srcEnd, dst); + } + + static void uninitializedCopy(const T* src, const T* srcEnd, T* dst) + { + VectorCopier<VectorTraits<T>::canCopyWithMemcpy, T>::uninitializedCopy(src, srcEnd, dst); + } + + static void uninitializedFill(T* dst, T* dstEnd, const T& val) + { + VectorFiller<VectorTraits<T>::canFillWithMemset, T>::uninitializedFill(dst, dstEnd, val); + } + + static bool compare(const T* a, const T* b, size_t size) + { + return VectorComparer<VectorTraits<T>::canCompareWithMemcmp, T>::compare(a, b, size); + } + }; + + template<typename T> + class VectorBufferBase { + WTF_MAKE_NONCOPYABLE(VectorBufferBase); + public: + void allocateBuffer(size_t newCapacity) + { + ASSERT(newCapacity); + m_capacity = newCapacity; + if (newCapacity > std::numeric_limits<size_t>::max() / sizeof(T)) + CRASH(); + m_buffer = static_cast<T*>(fastMalloc(newCapacity * sizeof(T))); + } + + bool tryAllocateBuffer(size_t newCapacity) + { + ASSERT(newCapacity); + if (newCapacity > std::numeric_limits<size_t>::max() / sizeof(T)) + return false; + + T* newBuffer; + if (tryFastMalloc(newCapacity * sizeof(T)).getValue(newBuffer)) { + m_capacity = newCapacity; + m_buffer = newBuffer; + return true; + } + return false; + } + + void deallocateBuffer(T* bufferToDeallocate) + { + if (m_buffer == bufferToDeallocate) { + m_buffer = 0; + m_capacity = 0; + } + fastFree(bufferToDeallocate); + } + + T* buffer() { return m_buffer; } + const T* buffer() const { return m_buffer; } + T** bufferSlot() { return &m_buffer; } + size_t capacity() const { return m_capacity; } + + T* releaseBuffer() + { + T* buffer = m_buffer; + m_buffer = 0; + m_capacity = 0; + return buffer; + } + + protected: + VectorBufferBase() + : m_buffer(0) + , m_capacity(0) + { + } + + VectorBufferBase(T* buffer, size_t capacity) + : m_buffer(buffer) + , m_capacity(capacity) + { + } + + ~VectorBufferBase() + { + // FIXME: It would be nice to find a way to ASSERT that m_buffer hasn't leaked here. + } + + T* m_buffer; + size_t m_capacity; + }; + + template<typename T, size_t inlineCapacity> + class VectorBuffer; + + template<typename T> + class VectorBuffer<T, 0> : private VectorBufferBase<T> { + private: + typedef VectorBufferBase<T> Base; + public: + VectorBuffer() + { + } + + VectorBuffer(size_t capacity) + { + // Calling malloc(0) might take a lock and may actually do an + // allocation on some systems. + if (capacity) + allocateBuffer(capacity); + } + + ~VectorBuffer() + { + deallocateBuffer(buffer()); + } + + void swap(VectorBuffer<T, 0>& other) + { + std::swap(m_buffer, other.m_buffer); + std::swap(m_capacity, other.m_capacity); + } + + void restoreInlineBufferIfNeeded() { } + + using Base::allocateBuffer; + using Base::tryAllocateBuffer; + using Base::deallocateBuffer; + + using Base::buffer; + using Base::bufferSlot; + using Base::capacity; + + using Base::releaseBuffer; + private: + using Base::m_buffer; + using Base::m_capacity; + }; + + template<typename T, size_t inlineCapacity> + class VectorBuffer : private VectorBufferBase<T> { + WTF_MAKE_NONCOPYABLE(VectorBuffer); + private: + typedef VectorBufferBase<T> Base; + public: + VectorBuffer() + : Base(inlineBuffer(), inlineCapacity) + { + } + + VectorBuffer(size_t capacity) + : Base(inlineBuffer(), inlineCapacity) + { + if (capacity > inlineCapacity) + Base::allocateBuffer(capacity); + } + + ~VectorBuffer() + { + deallocateBuffer(buffer()); + } + + void allocateBuffer(size_t newCapacity) + { + // FIXME: This should ASSERT(!m_buffer) to catch misuse/leaks. + if (newCapacity > inlineCapacity) + Base::allocateBuffer(newCapacity); + else { + m_buffer = inlineBuffer(); + m_capacity = inlineCapacity; + } + } + + bool tryAllocateBuffer(size_t newCapacity) + { + if (newCapacity > inlineCapacity) + return Base::tryAllocateBuffer(newCapacity); + m_buffer = inlineBuffer(); + m_capacity = inlineCapacity; + return true; + } + + void deallocateBuffer(T* bufferToDeallocate) + { + if (bufferToDeallocate == inlineBuffer()) + return; + Base::deallocateBuffer(bufferToDeallocate); + } + + void swap(VectorBuffer<T, inlineCapacity>& other) + { + if (buffer() == inlineBuffer() && other.buffer() == other.inlineBuffer()) { + WTF::swap(m_inlineBuffer, other.m_inlineBuffer); + std::swap(m_capacity, other.m_capacity); + } else if (buffer() == inlineBuffer()) { + m_buffer = other.m_buffer; + other.m_buffer = other.inlineBuffer(); + WTF::swap(m_inlineBuffer, other.m_inlineBuffer); + std::swap(m_capacity, other.m_capacity); + } else if (other.buffer() == other.inlineBuffer()) { + other.m_buffer = m_buffer; + m_buffer = inlineBuffer(); + WTF::swap(m_inlineBuffer, other.m_inlineBuffer); + std::swap(m_capacity, other.m_capacity); + } else { + std::swap(m_buffer, other.m_buffer); + std::swap(m_capacity, other.m_capacity); + } + } + + void restoreInlineBufferIfNeeded() + { + if (m_buffer) + return; + m_buffer = inlineBuffer(); + m_capacity = inlineCapacity; + } + + using Base::buffer; + using Base::bufferSlot; + using Base::capacity; + + T* releaseBuffer() + { + if (buffer() == inlineBuffer()) + return 0; + return Base::releaseBuffer(); + } + + private: + using Base::m_buffer; + using Base::m_capacity; + + static const size_t m_inlineBufferSize = inlineCapacity * sizeof(T); + T* inlineBuffer() { return reinterpret_cast_ptr<T*>(m_inlineBuffer.buffer); } + + AlignedBuffer<m_inlineBufferSize, WTF_ALIGN_OF(T)> m_inlineBuffer; + }; + + template<typename T, size_t inlineCapacity = 0> + class Vector { + WTF_MAKE_FAST_ALLOCATED; + private: + typedef VectorBuffer<T, inlineCapacity> Buffer; + typedef VectorTypeOperations<T> TypeOperations; + + class VectorReverseProxy; + + public: + typedef T ValueType; + + typedef T* iterator; + typedef const T* const_iterator; + typedef std::reverse_iterator<iterator> reverse_iterator; + typedef std::reverse_iterator<const_iterator> const_reverse_iterator; + + Vector() + : m_size(0) + { + } + + explicit Vector(size_t size) + : m_size(size) + , m_buffer(size) + { + if (begin()) + TypeOperations::initialize(begin(), end()); + } + + ~Vector() + { + if (m_size) + shrink(0); + } + + Vector(const Vector&); + template<size_t otherCapacity> + Vector(const Vector<T, otherCapacity>&); + + Vector& operator=(const Vector&); + template<size_t otherCapacity> + Vector& operator=(const Vector<T, otherCapacity>&); + + size_t size() const { return m_size; } + size_t capacity() const { return m_buffer.capacity(); } + bool isEmpty() const { return !size(); } + + T& at(size_t i) + { + ASSERT(i < size()); + return m_buffer.buffer()[i]; + } + const T& at(size_t i) const + { + ASSERT(i < size()); + return m_buffer.buffer()[i]; + } + + T& operator[](size_t i) { return at(i); } + const T& operator[](size_t i) const { return at(i); } + + T* data() { return m_buffer.buffer(); } + const T* data() const { return m_buffer.buffer(); } + T** dataSlot() { return m_buffer.bufferSlot(); } + + iterator begin() { return data(); } + iterator end() { return begin() + m_size; } + const_iterator begin() const { return data(); } + const_iterator end() const { return begin() + m_size; } + + reverse_iterator rbegin() { return reverse_iterator(end()); } + reverse_iterator rend() { return reverse_iterator(begin()); } + const_reverse_iterator rbegin() const { return const_reverse_iterator(end()); } + const_reverse_iterator rend() const { return const_reverse_iterator(begin()); } + + VectorReverseProxy& reversed() { return static_cast<VectorReverseProxy&>(*this); } + const VectorReverseProxy& reversed() const { return static_cast<const VectorReverseProxy&>(*this); } + + T& first() { return at(0); } + const T& first() const { return at(0); } + T& last() { return at(size() - 1); } + const T& last() const { return at(size() - 1); } + + template<typename U> bool contains(const U&) const; + template<typename U> size_t find(const U&) const; + template<typename U> size_t reverseFind(const U&) const; + + void shrink(size_t size); + void grow(size_t size); + void resize(size_t size); + void reserveCapacity(size_t newCapacity); + bool tryReserveCapacity(size_t newCapacity); + void reserveInitialCapacity(size_t initialCapacity); + void shrinkCapacity(size_t newCapacity); + void shrinkToFit() { shrinkCapacity(size()); } + + void clear() { shrinkCapacity(0); } + + template<typename U> void append(const U*, size_t); + template<typename U> void append(const U&); + template<typename U> void uncheckedAppend(const U& val); + template<size_t otherCapacity> void append(const Vector<T, otherCapacity>&); + template<typename U> bool tryAppend(const U*, size_t); + + template<typename U> void insert(size_t position, const U*, size_t); + template<typename U> void insert(size_t position, const U&); + template<typename U, size_t c> void insert(size_t position, const Vector<U, c>&); + + template<typename U> void prepend(const U*, size_t); + template<typename U> void prepend(const U&); + template<typename U, size_t c> void prepend(const Vector<U, c>&); + + void remove(size_t position); + void remove(size_t position, size_t length); + + void removeLast() + { + ASSERT(!isEmpty()); + shrink(size() - 1); + } + + Vector(size_t size, const T& val) + : m_size(size) + , m_buffer(size) + { + if (begin()) + TypeOperations::uninitializedFill(begin(), end(), val); + } + + void fill(const T&, size_t); + void fill(const T& val) { fill(val, size()); } + + template<typename Iterator> void appendRange(Iterator start, Iterator end); + + T* releaseBuffer(); + + void swap(Vector<T, inlineCapacity>& other) + { + std::swap(m_size, other.m_size); + m_buffer.swap(other.m_buffer); + } + + void reverse(); + + void checkConsistency(); + + private: + void expandCapacity(size_t newMinCapacity); + const T* expandCapacity(size_t newMinCapacity, const T*); + bool tryExpandCapacity(size_t newMinCapacity); + const T* tryExpandCapacity(size_t newMinCapacity, const T*); + template<typename U> U* expandCapacity(size_t newMinCapacity, U*); + template<typename U> void appendSlowCase(const U&); + + class VectorReverseProxy : private Vector { + public: + typedef typename Vector::reverse_iterator iterator; + typedef typename Vector::const_reverse_iterator const_iterator; + + iterator begin() { return Vector::rbegin(); } + iterator end() { return Vector::rend(); } + const_iterator begin() const { return Vector::rbegin(); } + const_iterator end() const { return Vector::rend(); } + + private: + friend class Vector; + + // These are intentionally not implemented. + VectorReverseProxy(); + VectorReverseProxy(const VectorReverseProxy&); + VectorReverseProxy& operator=(const VectorReverseProxy&); + ~VectorReverseProxy(); + }; + + size_t m_size; + Buffer m_buffer; + }; + +#if PLATFORM(QT) + template<typename T> + QDataStream& operator<<(QDataStream& stream, const Vector<T>& data) + { + stream << qint64(data.size()); + foreach (const T& i, data) + stream << i; + return stream; + } + + template<typename T> + QDataStream& operator>>(QDataStream& stream, Vector<T>& data) + { + data.clear(); + qint64 count; + T item; + stream >> count; + data.reserveCapacity(count); + for (qint64 i = 0; i < count; ++i) { + stream >> item; + data.append(item); + } + return stream; + } +#endif + + template<typename T, size_t inlineCapacity> + Vector<T, inlineCapacity>::Vector(const Vector& other) + : m_size(other.size()) + , m_buffer(other.capacity()) + { + if (begin()) + TypeOperations::uninitializedCopy(other.begin(), other.end(), begin()); + } + + template<typename T, size_t inlineCapacity> + template<size_t otherCapacity> + Vector<T, inlineCapacity>::Vector(const Vector<T, otherCapacity>& other) + : m_size(other.size()) + , m_buffer(other.capacity()) + { + if (begin()) + TypeOperations::uninitializedCopy(other.begin(), other.end(), begin()); + } + + template<typename T, size_t inlineCapacity> + Vector<T, inlineCapacity>& Vector<T, inlineCapacity>::operator=(const Vector<T, inlineCapacity>& other) + { + if (&other == this) + return *this; + + if (size() > other.size()) + shrink(other.size()); + else if (other.size() > capacity()) { + clear(); + reserveCapacity(other.size()); + if (!begin()) + return *this; + } + +// Works around an assert in VS2010. See https://connect.microsoft.com/VisualStudio/feedback/details/558044/std-copy-should-not-check-dest-when-first-last +#if COMPILER(MSVC) && defined(_ITERATOR_DEBUG_LEVEL) && _ITERATOR_DEBUG_LEVEL + if (!begin()) + return *this; +#endif + + std::copy(other.begin(), other.begin() + size(), begin()); + TypeOperations::uninitializedCopy(other.begin() + size(), other.end(), end()); + m_size = other.size(); + + return *this; + } + + inline bool typelessPointersAreEqual(const void* a, const void* b) { return a == b; } + + template<typename T, size_t inlineCapacity> + template<size_t otherCapacity> + Vector<T, inlineCapacity>& Vector<T, inlineCapacity>::operator=(const Vector<T, otherCapacity>& other) + { + // If the inline capacities match, we should call the more specific + // template. If the inline capacities don't match, the two objects + // shouldn't be allocated the same address. + ASSERT(!typelessPointersAreEqual(&other, this)); + + if (size() > other.size()) + shrink(other.size()); + else if (other.size() > capacity()) { + clear(); + reserveCapacity(other.size()); + if (!begin()) + return *this; + } + +// Works around an assert in VS2010. See https://connect.microsoft.com/VisualStudio/feedback/details/558044/std-copy-should-not-check-dest-when-first-last +#if COMPILER(MSVC) && defined(_ITERATOR_DEBUG_LEVEL) && _ITERATOR_DEBUG_LEVEL + if (!begin()) + return *this; +#endif + + std::copy(other.begin(), other.begin() + size(), begin()); + TypeOperations::uninitializedCopy(other.begin() + size(), other.end(), end()); + m_size = other.size(); + + return *this; + } + + template<typename T, size_t inlineCapacity> + template<typename U> + bool Vector<T, inlineCapacity>::contains(const U& value) const + { + return find(value) != notFound; + } + + template<typename T, size_t inlineCapacity> + template<typename U> + size_t Vector<T, inlineCapacity>::find(const U& value) const + { + for (size_t i = 0; i < size(); ++i) { + if (at(i) == value) + return i; + } + return notFound; + } + + template<typename T, size_t inlineCapacity> + template<typename U> + size_t Vector<T, inlineCapacity>::reverseFind(const U& value) const + { + for (size_t i = 1; i <= size(); ++i) { + const size_t index = size() - i; + if (at(index) == value) + return index; + } + return notFound; + } + + template<typename T, size_t inlineCapacity> + void Vector<T, inlineCapacity>::fill(const T& val, size_t newSize) + { + if (size() > newSize) + shrink(newSize); + else if (newSize > capacity()) { + clear(); + reserveCapacity(newSize); + if (!begin()) + return; + } + + std::fill(begin(), end(), val); + TypeOperations::uninitializedFill(end(), begin() + newSize, val); + m_size = newSize; + } + + template<typename T, size_t inlineCapacity> + template<typename Iterator> + void Vector<T, inlineCapacity>::appendRange(Iterator start, Iterator end) + { + for (Iterator it = start; it != end; ++it) + append(*it); + } + + template<typename T, size_t inlineCapacity> + void Vector<T, inlineCapacity>::expandCapacity(size_t newMinCapacity) + { + reserveCapacity(max(newMinCapacity, max(static_cast<size_t>(16), capacity() + capacity() / 4 + 1))); + } + + template<typename T, size_t inlineCapacity> + const T* Vector<T, inlineCapacity>::expandCapacity(size_t newMinCapacity, const T* ptr) + { + if (ptr < begin() || ptr >= end()) { + expandCapacity(newMinCapacity); + return ptr; + } + size_t index = ptr - begin(); + expandCapacity(newMinCapacity); + return begin() + index; + } + + template<typename T, size_t inlineCapacity> + bool Vector<T, inlineCapacity>::tryExpandCapacity(size_t newMinCapacity) + { + return tryReserveCapacity(max(newMinCapacity, max(static_cast<size_t>(16), capacity() + capacity() / 4 + 1))); + } + + template<typename T, size_t inlineCapacity> + const T* Vector<T, inlineCapacity>::tryExpandCapacity(size_t newMinCapacity, const T* ptr) + { + if (ptr < begin() || ptr >= end()) { + if (!tryExpandCapacity(newMinCapacity)) + return 0; + return ptr; + } + size_t index = ptr - begin(); + if (!tryExpandCapacity(newMinCapacity)) + return 0; + return begin() + index; + } + + template<typename T, size_t inlineCapacity> template<typename U> + inline U* Vector<T, inlineCapacity>::expandCapacity(size_t newMinCapacity, U* ptr) + { + expandCapacity(newMinCapacity); + return ptr; + } + + template<typename T, size_t inlineCapacity> + inline void Vector<T, inlineCapacity>::resize(size_t size) + { + if (size <= m_size) + TypeOperations::destruct(begin() + size, end()); + else { + if (size > capacity()) + expandCapacity(size); + if (begin()) + TypeOperations::initialize(end(), begin() + size); + } + + m_size = size; + } + + template<typename T, size_t inlineCapacity> + void Vector<T, inlineCapacity>::shrink(size_t size) + { + ASSERT(size <= m_size); + TypeOperations::destruct(begin() + size, end()); + m_size = size; + } + + template<typename T, size_t inlineCapacity> + void Vector<T, inlineCapacity>::grow(size_t size) + { + ASSERT(size >= m_size); + if (size > capacity()) + expandCapacity(size); + if (begin()) + TypeOperations::initialize(end(), begin() + size); + m_size = size; + } + + template<typename T, size_t inlineCapacity> + void Vector<T, inlineCapacity>::reserveCapacity(size_t newCapacity) + { + if (newCapacity <= capacity()) + return; + T* oldBuffer = begin(); + T* oldEnd = end(); + m_buffer.allocateBuffer(newCapacity); + if (begin()) + TypeOperations::move(oldBuffer, oldEnd, begin()); + m_buffer.deallocateBuffer(oldBuffer); + } + + template<typename T, size_t inlineCapacity> + bool Vector<T, inlineCapacity>::tryReserveCapacity(size_t newCapacity) + { + if (newCapacity <= capacity()) + return true; + T* oldBuffer = begin(); + T* oldEnd = end(); + if (!m_buffer.tryAllocateBuffer(newCapacity)) + return false; + ASSERT(begin()); + TypeOperations::move(oldBuffer, oldEnd, begin()); + m_buffer.deallocateBuffer(oldBuffer); + return true; + } + + template<typename T, size_t inlineCapacity> + inline void Vector<T, inlineCapacity>::reserveInitialCapacity(size_t initialCapacity) + { + ASSERT(!m_size); + ASSERT(capacity() == inlineCapacity); + if (initialCapacity > inlineCapacity) + m_buffer.allocateBuffer(initialCapacity); + } + + template<typename T, size_t inlineCapacity> + void Vector<T, inlineCapacity>::shrinkCapacity(size_t newCapacity) + { + if (newCapacity >= capacity()) + return; + + if (newCapacity < size()) + shrink(newCapacity); + + T* oldBuffer = begin(); + if (newCapacity > 0) { + T* oldEnd = end(); + m_buffer.allocateBuffer(newCapacity); + if (begin() != oldBuffer) + TypeOperations::move(oldBuffer, oldEnd, begin()); + } + + m_buffer.deallocateBuffer(oldBuffer); + m_buffer.restoreInlineBufferIfNeeded(); + } + + // Templatizing these is better than just letting the conversion happen implicitly, + // because for instance it allows a PassRefPtr to be appended to a RefPtr vector + // without refcount thrash. + + template<typename T, size_t inlineCapacity> template<typename U> + void Vector<T, inlineCapacity>::append(const U* data, size_t dataSize) + { + size_t newSize = m_size + dataSize; + if (newSize > capacity()) { + data = expandCapacity(newSize, data); + if (!begin()) + return; + } + if (newSize < m_size) + CRASH(); + T* dest = end(); + for (size_t i = 0; i < dataSize; ++i) + new (NotNull, &dest[i]) T(data[i]); + m_size = newSize; + } + + template<typename T, size_t inlineCapacity> template<typename U> + bool Vector<T, inlineCapacity>::tryAppend(const U* data, size_t dataSize) + { + size_t newSize = m_size + dataSize; + if (newSize > capacity()) { + data = tryExpandCapacity(newSize, data); + if (!data) + return false; + ASSERT(begin()); + } + if (newSize < m_size) + return false; + T* dest = end(); + for (size_t i = 0; i < dataSize; ++i) + new (NotNull, &dest[i]) T(data[i]); + m_size = newSize; + return true; + } + + template<typename T, size_t inlineCapacity> template<typename U> + ALWAYS_INLINE void Vector<T, inlineCapacity>::append(const U& val) + { + if (size() != capacity()) { + new (NotNull, end()) T(val); + ++m_size; + return; + } + + appendSlowCase(val); + } + + template<typename T, size_t inlineCapacity> template<typename U> + void Vector<T, inlineCapacity>::appendSlowCase(const U& val) + { + ASSERT(size() == capacity()); + + const U* ptr = &val; + ptr = expandCapacity(size() + 1, ptr); + if (!begin()) + return; + + new (NotNull, end()) T(*ptr); + ++m_size; + } + + // This version of append saves a branch in the case where you know that the + // vector's capacity is large enough for the append to succeed. + + template<typename T, size_t inlineCapacity> template<typename U> + inline void Vector<T, inlineCapacity>::uncheckedAppend(const U& val) + { + ASSERT(size() < capacity()); + const U* ptr = &val; + new (NotNull, end()) T(*ptr); + ++m_size; + } + + // This method should not be called append, a better name would be appendElements. + // It could also be eliminated entirely, and call sites could just use + // appendRange(val.begin(), val.end()). + template<typename T, size_t inlineCapacity> template<size_t otherCapacity> + inline void Vector<T, inlineCapacity>::append(const Vector<T, otherCapacity>& val) + { + append(val.begin(), val.size()); + } + + template<typename T, size_t inlineCapacity> template<typename U> + void Vector<T, inlineCapacity>::insert(size_t position, const U* data, size_t dataSize) + { + ASSERT(position <= size()); + size_t newSize = m_size + dataSize; + if (newSize > capacity()) { + data = expandCapacity(newSize, data); + if (!begin()) + return; + } + if (newSize < m_size) + CRASH(); + T* spot = begin() + position; + TypeOperations::moveOverlapping(spot, end(), spot + dataSize); + for (size_t i = 0; i < dataSize; ++i) + new (NotNull, &spot[i]) T(data[i]); + m_size = newSize; + } + + template<typename T, size_t inlineCapacity> template<typename U> + inline void Vector<T, inlineCapacity>::insert(size_t position, const U& val) + { + ASSERT(position <= size()); + const U* data = &val; + if (size() == capacity()) { + data = expandCapacity(size() + 1, data); + if (!begin()) + return; + } + T* spot = begin() + position; + TypeOperations::moveOverlapping(spot, end(), spot + 1); + new (NotNull, spot) T(*data); + ++m_size; + } + + template<typename T, size_t inlineCapacity> template<typename U, size_t c> + inline void Vector<T, inlineCapacity>::insert(size_t position, const Vector<U, c>& val) + { + insert(position, val.begin(), val.size()); + } + + template<typename T, size_t inlineCapacity> template<typename U> + void Vector<T, inlineCapacity>::prepend(const U* data, size_t dataSize) + { + insert(0, data, dataSize); + } + + template<typename T, size_t inlineCapacity> template<typename U> + inline void Vector<T, inlineCapacity>::prepend(const U& val) + { + insert(0, val); + } + + template<typename T, size_t inlineCapacity> template<typename U, size_t c> + inline void Vector<T, inlineCapacity>::prepend(const Vector<U, c>& val) + { + insert(0, val.begin(), val.size()); + } + + template<typename T, size_t inlineCapacity> + inline void Vector<T, inlineCapacity>::remove(size_t position) + { + ASSERT(position < size()); + T* spot = begin() + position; + spot->~T(); + TypeOperations::moveOverlapping(spot + 1, end(), spot); + --m_size; + } + + template<typename T, size_t inlineCapacity> + inline void Vector<T, inlineCapacity>::remove(size_t position, size_t length) + { + ASSERT(position < size()); + ASSERT(position + length <= size()); + T* beginSpot = begin() + position; + T* endSpot = beginSpot + length; + TypeOperations::destruct(beginSpot, endSpot); + TypeOperations::moveOverlapping(endSpot, end(), beginSpot); + m_size -= length; + } + + template<typename T, size_t inlineCapacity> + inline void Vector<T, inlineCapacity>::reverse() + { + for (size_t i = 0; i < m_size / 2; ++i) + std::swap(at(i), at(m_size - 1 - i)); + } + + template<typename T, size_t inlineCapacity> + inline T* Vector<T, inlineCapacity>::releaseBuffer() + { + T* buffer = m_buffer.releaseBuffer(); + if (inlineCapacity && !buffer && m_size) { + // If the vector had some data, but no buffer to release, + // that means it was using the inline buffer. In that case, + // we create a brand new buffer so the caller always gets one. + size_t bytes = m_size * sizeof(T); + buffer = static_cast<T*>(fastMalloc(bytes)); + memcpy(buffer, data(), bytes); + } + m_size = 0; + return buffer; + } + + template<typename T, size_t inlineCapacity> + inline void Vector<T, inlineCapacity>::checkConsistency() + { +#if !ASSERT_DISABLED + for (size_t i = 0; i < size(); ++i) + ValueCheck<T>::checkConsistency(at(i)); +#endif + } + + template<typename T, size_t inlineCapacity> + void deleteAllValues(const Vector<T, inlineCapacity>& collection) + { + typedef typename Vector<T, inlineCapacity>::const_iterator iterator; + iterator end = collection.end(); + for (iterator it = collection.begin(); it != end; ++it) + delete *it; + } + + template<typename T, size_t inlineCapacity> + inline void swap(Vector<T, inlineCapacity>& a, Vector<T, inlineCapacity>& b) + { + a.swap(b); + } + + template<typename T, size_t inlineCapacity> + bool operator==(const Vector<T, inlineCapacity>& a, const Vector<T, inlineCapacity>& b) + { + if (a.size() != b.size()) + return false; + + return VectorTypeOperations<T>::compare(a.data(), b.data(), a.size()); + } + + template<typename T, size_t inlineCapacity> + inline bool operator!=(const Vector<T, inlineCapacity>& a, const Vector<T, inlineCapacity>& b) + { + return !(a == b); + } + +#if !ASSERT_DISABLED + template<typename T> struct ValueCheck<Vector<T> > { + typedef Vector<T> TraitType; + static void checkConsistency(const Vector<T>& v) + { + v.checkConsistency(); + } + }; +#endif + +} // namespace WTF + +using WTF::Vector; + +#endif // WTF_Vector_h diff --git a/Source/JavaScriptCore/wtf/VectorTraits.h b/Source/JavaScriptCore/wtf/VectorTraits.h new file mode 100644 index 000000000..6777c9ebf --- /dev/null +++ b/Source/JavaScriptCore/wtf/VectorTraits.h @@ -0,0 +1,99 @@ +/* + * Copyright (C) 2006, 2007, 2008 Apple Inc. All rights reserved. + * + * This library 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 library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#ifndef WTF_VectorTraits_h +#define WTF_VectorTraits_h + +#include "OwnPtr.h" +#include "RefPtr.h" +#include "TypeTraits.h" +#include <utility> +#include <memory> + +using std::pair; + +namespace WTF { + + template<bool isPod, typename T> + struct VectorTraitsBase; + + template<typename T> + struct VectorTraitsBase<false, T> + { + static const bool needsDestruction = true; + static const bool needsInitialization = true; + static const bool canInitializeWithMemset = false; + static const bool canMoveWithMemcpy = false; + static const bool canCopyWithMemcpy = false; + static const bool canFillWithMemset = false; + static const bool canCompareWithMemcmp = false; + }; + + template<typename T> + struct VectorTraitsBase<true, T> + { + static const bool needsDestruction = false; + static const bool needsInitialization = false; + static const bool canInitializeWithMemset = false; + static const bool canMoveWithMemcpy = true; + static const bool canCopyWithMemcpy = true; + static const bool canFillWithMemset = sizeof(T) == sizeof(char); + static const bool canCompareWithMemcmp = true; + }; + + template<typename T> + struct VectorTraits : VectorTraitsBase<IsPod<T>::value, T> { }; + + struct SimpleClassVectorTraits : VectorTraitsBase<false, void> + { + static const bool canInitializeWithMemset = true; + static const bool canMoveWithMemcpy = true; + static const bool canCompareWithMemcmp = true; + }; + + // we know OwnPtr and RefPtr are simple enough that initializing to 0 and moving with memcpy + // (and then not destructing the original) will totally work + template<typename P> + struct VectorTraits<RefPtr<P> > : SimpleClassVectorTraits { }; + + template<typename P> + struct VectorTraits<OwnPtr<P> > : SimpleClassVectorTraits { }; + + template<typename First, typename Second> + struct VectorTraits<pair<First, Second> > + { + typedef VectorTraits<First> FirstTraits; + typedef VectorTraits<Second> SecondTraits; + + static const bool needsDestruction = FirstTraits::needsDestruction || SecondTraits::needsDestruction; + static const bool needsInitialization = FirstTraits::needsInitialization || SecondTraits::needsInitialization; + static const bool canInitializeWithMemset = FirstTraits::canInitializeWithMemset && SecondTraits::canInitializeWithMemset; + static const bool canMoveWithMemcpy = FirstTraits::canMoveWithMemcpy && SecondTraits::canMoveWithMemcpy; + static const bool canCopyWithMemcpy = FirstTraits::canCopyWithMemcpy && SecondTraits::canCopyWithMemcpy; + static const bool canFillWithMemset = false; + static const bool canCompareWithMemcmp = FirstTraits::canCompareWithMemcmp && SecondTraits::canCompareWithMemcmp; + }; + +} // namespace WTF + +using WTF::VectorTraits; +using WTF::SimpleClassVectorTraits; + +#endif // WTF_VectorTraits_h diff --git a/Source/JavaScriptCore/wtf/WTFThreadData.cpp b/Source/JavaScriptCore/wtf/WTFThreadData.cpp new file mode 100644 index 000000000..241fbe449 --- /dev/null +++ b/Source/JavaScriptCore/wtf/WTFThreadData.cpp @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2008, 2010 Apple Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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 APPLE COMPUTER, INC. ``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 APPLE COMPUTER, INC. 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 "config.h" +#include "WTFThreadData.h" + +namespace WTF { + +ThreadSpecific<WTFThreadData>* WTFThreadData::staticData; + +WTFThreadData::WTFThreadData() + : m_atomicStringTable(0) + , m_atomicStringTableDestructor(0) +#if USE(JSC) + , m_defaultIdentifierTable(new JSC::IdentifierTable()) + , m_currentIdentifierTable(m_defaultIdentifierTable) + , m_stackBounds(StackBounds::currentThreadStackBounds()) +#endif +{ +} + +WTFThreadData::~WTFThreadData() +{ + if (m_atomicStringTableDestructor) + m_atomicStringTableDestructor(m_atomicStringTable); +#if USE(JSC) + delete m_defaultIdentifierTable; +#endif +} + +} diff --git a/Source/JavaScriptCore/wtf/WTFThreadData.h b/Source/JavaScriptCore/wtf/WTFThreadData.h new file mode 100644 index 000000000..81b817f6f --- /dev/null +++ b/Source/JavaScriptCore/wtf/WTFThreadData.h @@ -0,0 +1,146 @@ +/* + * Copyright (C) 2008 Apple Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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 APPLE COMPUTER, INC. ``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 APPLE COMPUTER, INC. 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. + * + */ + +#ifndef WTFThreadData_h +#define WTFThreadData_h + +#include <wtf/HashMap.h> +#include <wtf/HashSet.h> +#include <wtf/Noncopyable.h> +#include <wtf/StackBounds.h> +#include <wtf/text/StringHash.h> +#include <wtf/ThreadSpecific.h> +#include <wtf/Threading.h> + +#if USE(JSC) +// FIXME: This is a temporary layering violation while we move more string code to WTF. +namespace JSC { + +typedef HashMap<const char*, RefPtr<StringImpl>, PtrHash<const char*> > LiteralIdentifierTable; + +class IdentifierTable { + WTF_MAKE_FAST_ALLOCATED; +public: + ~IdentifierTable(); + + std::pair<HashSet<StringImpl*>::iterator, bool> add(StringImpl* value); + template<typename U, typename V> + std::pair<HashSet<StringImpl*>::iterator, bool> add(U value); + + bool remove(StringImpl* r) + { + HashSet<StringImpl*>::iterator iter = m_table.find(r); + if (iter == m_table.end()) + return false; + m_table.remove(iter); + return true; + } + + LiteralIdentifierTable& literalTable() { return m_literalTable; } + +private: + HashSet<StringImpl*> m_table; + LiteralIdentifierTable m_literalTable; +}; + +} +#endif + +namespace WTF { + +class AtomicStringTable; + +typedef void (*AtomicStringTableDestructor)(AtomicStringTable*); + +class WTFThreadData { + WTF_MAKE_NONCOPYABLE(WTFThreadData); +public: + WTFThreadData(); + ~WTFThreadData(); + + AtomicStringTable* atomicStringTable() + { + return m_atomicStringTable; + } + +#if USE(JSC) + JSC::IdentifierTable* currentIdentifierTable() + { + return m_currentIdentifierTable; + } + + JSC::IdentifierTable* setCurrentIdentifierTable(JSC::IdentifierTable* identifierTable) + { + JSC::IdentifierTable* oldIdentifierTable = m_currentIdentifierTable; + m_currentIdentifierTable = identifierTable; + return oldIdentifierTable; + } + + void resetCurrentIdentifierTable() + { + m_currentIdentifierTable = m_defaultIdentifierTable; + } + + const StackBounds& stack() const + { + return m_stackBounds; + } +#endif + +private: + AtomicStringTable* m_atomicStringTable; + AtomicStringTableDestructor m_atomicStringTableDestructor; + +#if USE(JSC) + JSC::IdentifierTable* m_defaultIdentifierTable; + JSC::IdentifierTable* m_currentIdentifierTable; + StackBounds m_stackBounds; +#endif + + static WTF_EXPORTDATA ThreadSpecific<WTFThreadData>* staticData; + friend WTFThreadData& wtfThreadData(); + friend class AtomicStringTable; +}; + +inline WTFThreadData& wtfThreadData() +{ + // WRT WebCore: + // WTFThreadData is used on main thread before it could possibly be used + // on secondary ones, so there is no need for synchronization here. + // WRT JavaScriptCore: + // wtfThreadData() is initially called from initializeThreading(), ensuring + // this is initially called in a pthread_once locked context. + if (!WTFThreadData::staticData) + WTFThreadData::staticData = new ThreadSpecific<WTFThreadData>; + return **WTFThreadData::staticData; +} + +} // namespace WTF + +using WTF::WTFThreadData; +using WTF::wtfThreadData; + +#endif // WTFThreadData_h diff --git a/Source/JavaScriptCore/wtf/blackberry/MainThreadBlackBerry.cpp b/Source/JavaScriptCore/wtf/blackberry/MainThreadBlackBerry.cpp new file mode 100644 index 000000000..7284f4b44 --- /dev/null +++ b/Source/JavaScriptCore/wtf/blackberry/MainThreadBlackBerry.cpp @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2009, 2010, 2011 Research In Motion Limited. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "config.h" +#include "MainThread.h" + +#include <BlackBerryPlatformClient.h> + +namespace WTF { + +void initializeMainThreadPlatform() +{ +} + +void scheduleDispatchFunctionsOnMainThread() +{ + BlackBerry::Platform::Client::get()->scheduleCallOnMainThread(dispatchFunctionsFromMainThread); +} + +} // namespace WTF diff --git a/Source/JavaScriptCore/wtf/chromium/ChromiumThreading.h b/Source/JavaScriptCore/wtf/chromium/ChromiumThreading.h new file mode 100644 index 000000000..39386219d --- /dev/null +++ b/Source/JavaScriptCore/wtf/chromium/ChromiumThreading.h @@ -0,0 +1,44 @@ +/* +* Copyright (C) 2009 Google Inc. 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 Google Inc. 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. +*/ + +#ifndef ChromiumThreading_h +#define ChromiumThreading_h + +namespace WTF { + +// An interface to the embedding layer, which provides threading support. +class ChromiumThreading { +public: + static void callOnMainThread(void (*func)(void*), void* context); +}; + +} // namespace WTF + +#endif // ChromiumThreading_h diff --git a/Source/JavaScriptCore/wtf/chromium/MainThreadChromium.cpp b/Source/JavaScriptCore/wtf/chromium/MainThreadChromium.cpp new file mode 100644 index 000000000..9e6592b57 --- /dev/null +++ b/Source/JavaScriptCore/wtf/chromium/MainThreadChromium.cpp @@ -0,0 +1,73 @@ +/* +* Copyright (C) 2009 Google Inc. 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 Google Inc. 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 "config.h" +#include "MainThread.h" + +#include "Assertions.h" +#include "ChromiumThreading.h" +#include "Threading.h" + +namespace WTF { + +static ThreadIdentifier mainThreadIdentifier; + +void initializeMainThread() +{ + static bool initializedMainThread; + if (initializedMainThread) + return; + initializedMainThread = true; + + mainThreadIdentifier = currentThread(); +} + +void callOnMainThread(MainThreadFunction* function, void* context) +{ + ChromiumThreading::callOnMainThread(function, context); +} + +void callOnMainThreadAndWait(MainThreadFunction*, void*) +{ + ASSERT_NOT_REACHED(); +} + +void setMainThreadCallbacksPaused(bool) +{ + ASSERT_NOT_REACHED(); +} + +bool isMainThread() +{ + return currentThread() == mainThreadIdentifier; +} + +} // namespace WTF + diff --git a/Source/JavaScriptCore/wtf/dtoa.cpp b/Source/JavaScriptCore/wtf/dtoa.cpp new file mode 100644 index 000000000..3732fe614 --- /dev/null +++ b/Source/JavaScriptCore/wtf/dtoa.cpp @@ -0,0 +1,1867 @@ +/**************************************************************** + * + * The author of this software is David M. Gay. + * + * Copyright (c) 1991, 2000, 2001 by Lucent Technologies. + * Copyright (C) 2002, 2005, 2006, 2007, 2008, 2010 Apple Inc. All rights reserved. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose without fee is hereby granted, provided that this entire notice + * is included in all copies of any software which is or includes a copy + * or modification of this software and in all copies of the supporting + * documentation for such software. + * + * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTY. IN PARTICULAR, NEITHER THE AUTHOR NOR LUCENT MAKES ANY + * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY + * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. + * + ***************************************************************/ + +/* Please send bug reports to David M. Gay (dmg at acm dot org, + * with " at " changed at "@" and " dot " changed to "."). */ + +/* On a machine with IEEE extended-precision registers, it is + * necessary to specify double-precision (53-bit) rounding precision + * before invoking strtod or dtoa. If the machine uses (the equivalent + * of) Intel 80x87 arithmetic, the call + * _control87(PC_53, MCW_PC); + * does this with many compilers. Whether this or another call is + * appropriate depends on the compiler; for this to work, it may be + * necessary to #include "float.h" or another system-dependent header + * file. + */ + +/* strtod for IEEE-arithmetic machines. + * + * This strtod returns a nearest machine number to the input decimal + * string (or sets errno to ERANGE). With IEEE arithmetic, ties are + * broken by the IEEE round-even rule. Otherwise ties are broken by + * biased rounding (add half and chop). + * + * Inspired loosely by William D. Clinger's paper "How to Read Floating + * Point Numbers Accurately" [Proc. ACM SIGPLAN '90, pp. 92-101]. + * + * Modifications: + * + * 1. We only require IEEE double-precision arithmetic (not IEEE double-extended). + * 2. We get by with floating-point arithmetic in a case that + * Clinger missed -- when we're computing d * 10^n + * for a small integer d and the integer n is not too + * much larger than 22 (the maximum integer k for which + * we can represent 10^k exactly), we may be able to + * compute (d*10^k) * 10^(e-k) with just one roundoff. + * 3. Rather than a bit-at-a-time adjustment of the binary + * result in the hard case, we use floating-point + * arithmetic to determine the adjustment to within + * one bit; only in really hard cases do we need to + * compute a second residual. + * 4. Because of 3., we don't need a large table of powers of 10 + * for ten-to-e (just some small tables, e.g. of 10^k + * for 0 <= k <= 22). + */ + +#include "config.h" +#include "dtoa.h" + +#if HAVE(ERRNO_H) +#include <errno.h> +#endif +#include <float.h> +#include <math.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <wtf/AlwaysInline.h> +#include <wtf/Assertions.h> +#include <wtf/FastMalloc.h> +#include <wtf/MathExtras.h> +#include <wtf/Threading.h> +#include <wtf/UnusedParam.h> +#include <wtf/Vector.h> +#include <wtf/dtoa/double-conversion.h> + +#if COMPILER(MSVC) +#pragma warning(disable: 4244) +#pragma warning(disable: 4245) +#pragma warning(disable: 4554) +#endif + +namespace WTF { + +Mutex* s_dtoaP5Mutex; + +typedef union { + double d; + uint32_t L[2]; +} U; + +#if CPU(BIG_ENDIAN) || CPU(MIDDLE_ENDIAN) +#define word0(x) (x)->L[0] +#define word1(x) (x)->L[1] +#else +#define word0(x) (x)->L[1] +#define word1(x) (x)->L[0] +#endif +#define dval(x) (x)->d + +/* The following definition of Storeinc is appropriate for MIPS processors. + * An alternative that might be better on some machines is + * *p++ = high << 16 | low & 0xffff; + */ +static ALWAYS_INLINE uint32_t* storeInc(uint32_t* p, uint16_t high, uint16_t low) +{ + uint16_t* p16 = reinterpret_cast<uint16_t*>(p); +#if CPU(BIG_ENDIAN) + p16[0] = high; + p16[1] = low; +#else + p16[1] = high; + p16[0] = low; +#endif + return p + 1; +} + +#define Exp_shift 20 +#define Exp_shift1 20 +#define Exp_msk1 0x100000 +#define Exp_msk11 0x100000 +#define Exp_mask 0x7ff00000 +#define P 53 +#define Bias 1023 +#define Emin (-1022) +#define Exp_1 0x3ff00000 +#define Exp_11 0x3ff00000 +#define Ebits 11 +#define Frac_mask 0xfffff +#define Frac_mask1 0xfffff +#define Ten_pmax 22 +#define Bletch 0x10 +#define Bndry_mask 0xfffff +#define Bndry_mask1 0xfffff +#define LSB 1 +#define Sign_bit 0x80000000 +#define Log2P 1 +#define Tiny0 0 +#define Tiny1 1 +#define Quick_max 14 +#define Int_max 14 + +#define rounded_product(a, b) a *= b +#define rounded_quotient(a, b) a /= b + +#define Big0 (Frac_mask1 | Exp_msk1 * (DBL_MAX_EXP + Bias - 1)) +#define Big1 0xffffffff + +#if CPU(PPC64) || CPU(X86_64) +// FIXME: should we enable this on all 64-bit CPUs? +// 64-bit emulation provided by the compiler is likely to be slower than dtoa own code on 32-bit hardware. +#define USE_LONG_LONG +#endif + +struct BigInt { + BigInt() : sign(0) { } + int sign; + + void clear() + { + sign = 0; + m_words.clear(); + } + + size_t size() const + { + return m_words.size(); + } + + void resize(size_t s) + { + m_words.resize(s); + } + + uint32_t* words() + { + return m_words.data(); + } + + const uint32_t* words() const + { + return m_words.data(); + } + + void append(uint32_t w) + { + m_words.append(w); + } + + Vector<uint32_t, 16> m_words; +}; + +static void multadd(BigInt& b, int m, int a) /* multiply by m and add a */ +{ +#ifdef USE_LONG_LONG + unsigned long long carry; +#else + uint32_t carry; +#endif + + int wds = b.size(); + uint32_t* x = b.words(); + int i = 0; + carry = a; + do { +#ifdef USE_LONG_LONG + unsigned long long y = *x * (unsigned long long)m + carry; + carry = y >> 32; + *x++ = (uint32_t)y & 0xffffffffUL; +#else + uint32_t xi = *x; + uint32_t y = (xi & 0xffff) * m + carry; + uint32_t z = (xi >> 16) * m + (y >> 16); + carry = z >> 16; + *x++ = (z << 16) + (y & 0xffff); +#endif + } while (++i < wds); + + if (carry) + b.append((uint32_t)carry); +} + +static void s2b(BigInt& b, const char* s, int nd0, int nd, uint32_t y9) +{ + b.sign = 0; + b.resize(1); + b.words()[0] = y9; + + int i = 9; + if (9 < nd0) { + s += 9; + do { + multadd(b, 10, *s++ - '0'); + } while (++i < nd0); + s++; + } else + s += 10; + for (; i < nd; i++) + multadd(b, 10, *s++ - '0'); +} + +static int hi0bits(uint32_t x) +{ + int k = 0; + + if (!(x & 0xffff0000)) { + k = 16; + x <<= 16; + } + if (!(x & 0xff000000)) { + k += 8; + x <<= 8; + } + if (!(x & 0xf0000000)) { + k += 4; + x <<= 4; + } + if (!(x & 0xc0000000)) { + k += 2; + x <<= 2; + } + if (!(x & 0x80000000)) { + k++; + if (!(x & 0x40000000)) + return 32; + } + return k; +} + +static int lo0bits(uint32_t* y) +{ + int k; + uint32_t x = *y; + + if (x & 7) { + if (x & 1) + return 0; + if (x & 2) { + *y = x >> 1; + return 1; + } + *y = x >> 2; + return 2; + } + k = 0; + if (!(x & 0xffff)) { + k = 16; + x >>= 16; + } + if (!(x & 0xff)) { + k += 8; + x >>= 8; + } + if (!(x & 0xf)) { + k += 4; + x >>= 4; + } + if (!(x & 0x3)) { + k += 2; + x >>= 2; + } + if (!(x & 1)) { + k++; + x >>= 1; + if (!x) + return 32; + } + *y = x; + return k; +} + +static void i2b(BigInt& b, int i) +{ + b.sign = 0; + b.resize(1); + b.words()[0] = i; +} + +static void mult(BigInt& aRef, const BigInt& bRef) +{ + const BigInt* a = &aRef; + const BigInt* b = &bRef; + BigInt c; + int wa, wb, wc; + const uint32_t* x = 0; + const uint32_t* xa; + const uint32_t* xb; + const uint32_t* xae; + const uint32_t* xbe; + uint32_t* xc; + uint32_t* xc0; + uint32_t y; +#ifdef USE_LONG_LONG + unsigned long long carry, z; +#else + uint32_t carry, z; +#endif + + if (a->size() < b->size()) { + const BigInt* tmp = a; + a = b; + b = tmp; + } + + wa = a->size(); + wb = b->size(); + wc = wa + wb; + c.resize(wc); + + for (xc = c.words(), xa = xc + wc; xc < xa; xc++) + *xc = 0; + xa = a->words(); + xae = xa + wa; + xb = b->words(); + xbe = xb + wb; + xc0 = c.words(); +#ifdef USE_LONG_LONG + for (; xb < xbe; xc0++) { + if ((y = *xb++)) { + x = xa; + xc = xc0; + carry = 0; + do { + z = *x++ * (unsigned long long)y + *xc + carry; + carry = z >> 32; + *xc++ = (uint32_t)z & 0xffffffffUL; + } while (x < xae); + *xc = (uint32_t)carry; + } + } +#else + for (; xb < xbe; xb++, xc0++) { + if ((y = *xb & 0xffff)) { + x = xa; + xc = xc0; + carry = 0; + do { + z = (*x & 0xffff) * y + (*xc & 0xffff) + carry; + carry = z >> 16; + uint32_t z2 = (*x++ >> 16) * y + (*xc >> 16) + carry; + carry = z2 >> 16; + xc = storeInc(xc, z2, z); + } while (x < xae); + *xc = carry; + } + if ((y = *xb >> 16)) { + x = xa; + xc = xc0; + carry = 0; + uint32_t z2 = *xc; + do { + z = (*x & 0xffff) * y + (*xc >> 16) + carry; + carry = z >> 16; + xc = storeInc(xc, z, z2); + z2 = (*x++ >> 16) * y + (*xc & 0xffff) + carry; + carry = z2 >> 16; + } while (x < xae); + *xc = z2; + } + } +#endif + for (xc0 = c.words(), xc = xc0 + wc; wc > 0 && !*--xc; --wc) { } + c.resize(wc); + aRef = c; +} + +struct P5Node { + WTF_MAKE_NONCOPYABLE(P5Node); WTF_MAKE_FAST_ALLOCATED; +public: + P5Node() { } + BigInt val; + P5Node* next; +}; + +static P5Node* p5s; +static int p5sCount; + +static ALWAYS_INLINE void pow5mult(BigInt& b, int k) +{ + static int p05[3] = { 5, 25, 125 }; + + if (int i = k & 3) + multadd(b, p05[i - 1], 0); + + if (!(k >>= 2)) + return; + + s_dtoaP5Mutex->lock(); + P5Node* p5 = p5s; + + if (!p5) { + /* first time */ + p5 = new P5Node; + i2b(p5->val, 625); + p5->next = 0; + p5s = p5; + p5sCount = 1; + } + + int p5sCountLocal = p5sCount; + s_dtoaP5Mutex->unlock(); + int p5sUsed = 0; + + for (;;) { + if (k & 1) + mult(b, p5->val); + + if (!(k >>= 1)) + break; + + if (++p5sUsed == p5sCountLocal) { + s_dtoaP5Mutex->lock(); + if (p5sUsed == p5sCount) { + ASSERT(!p5->next); + p5->next = new P5Node; + p5->next->next = 0; + p5->next->val = p5->val; + mult(p5->next->val, p5->next->val); + ++p5sCount; + } + + p5sCountLocal = p5sCount; + s_dtoaP5Mutex->unlock(); + } + p5 = p5->next; + } +} + +static ALWAYS_INLINE void lshift(BigInt& b, int k) +{ + int n = k >> 5; + + int origSize = b.size(); + int n1 = n + origSize + 1; + + if (k &= 0x1f) + b.resize(b.size() + n + 1); + else + b.resize(b.size() + n); + + const uint32_t* srcStart = b.words(); + uint32_t* dstStart = b.words(); + const uint32_t* src = srcStart + origSize - 1; + uint32_t* dst = dstStart + n1 - 1; + if (k) { + uint32_t hiSubword = 0; + int s = 32 - k; + for (; src >= srcStart; --src) { + *dst-- = hiSubword | *src >> s; + hiSubword = *src << k; + } + *dst = hiSubword; + ASSERT(dst == dstStart + n); + + b.resize(origSize + n + !!b.words()[n1 - 1]); + } + else { + do { + *--dst = *src--; + } while (src >= srcStart); + } + for (dst = dstStart + n; dst != dstStart; ) + *--dst = 0; + + ASSERT(b.size() <= 1 || b.words()[b.size() - 1]); +} + +static int cmp(const BigInt& a, const BigInt& b) +{ + const uint32_t *xa, *xa0, *xb, *xb0; + int i, j; + + i = a.size(); + j = b.size(); + ASSERT(i <= 1 || a.words()[i - 1]); + ASSERT(j <= 1 || b.words()[j - 1]); + if (i -= j) + return i; + xa0 = a.words(); + xa = xa0 + j; + xb0 = b.words(); + xb = xb0 + j; + for (;;) { + if (*--xa != *--xb) + return *xa < *xb ? -1 : 1; + if (xa <= xa0) + break; + } + return 0; +} + +static ALWAYS_INLINE void diff(BigInt& c, const BigInt& aRef, const BigInt& bRef) +{ + const BigInt* a = &aRef; + const BigInt* b = &bRef; + int i, wa, wb; + uint32_t* xc; + + i = cmp(*a, *b); + if (!i) { + c.sign = 0; + c.resize(1); + c.words()[0] = 0; + return; + } + if (i < 0) { + const BigInt* tmp = a; + a = b; + b = tmp; + i = 1; + } else + i = 0; + + wa = a->size(); + const uint32_t* xa = a->words(); + const uint32_t* xae = xa + wa; + wb = b->size(); + const uint32_t* xb = b->words(); + const uint32_t* xbe = xb + wb; + + c.resize(wa); + c.sign = i; + xc = c.words(); +#ifdef USE_LONG_LONG + unsigned long long borrow = 0; + do { + unsigned long long y = (unsigned long long)*xa++ - *xb++ - borrow; + borrow = y >> 32 & (uint32_t)1; + *xc++ = (uint32_t)y & 0xffffffffUL; + } while (xb < xbe); + while (xa < xae) { + unsigned long long y = *xa++ - borrow; + borrow = y >> 32 & (uint32_t)1; + *xc++ = (uint32_t)y & 0xffffffffUL; + } +#else + uint32_t borrow = 0; + do { + uint32_t y = (*xa & 0xffff) - (*xb & 0xffff) - borrow; + borrow = (y & 0x10000) >> 16; + uint32_t z = (*xa++ >> 16) - (*xb++ >> 16) - borrow; + borrow = (z & 0x10000) >> 16; + xc = storeInc(xc, z, y); + } while (xb < xbe); + while (xa < xae) { + uint32_t y = (*xa & 0xffff) - borrow; + borrow = (y & 0x10000) >> 16; + uint32_t z = (*xa++ >> 16) - borrow; + borrow = (z & 0x10000) >> 16; + xc = storeInc(xc, z, y); + } +#endif + while (!*--xc) + wa--; + c.resize(wa); +} + +static double ulp(U *x) +{ + register int32_t L; + U u; + + L = (word0(x) & Exp_mask) - (P - 1) * Exp_msk1; + word0(&u) = L; + word1(&u) = 0; + return dval(&u); +} + +static double b2d(const BigInt& a, int* e) +{ + const uint32_t* xa; + const uint32_t* xa0; + uint32_t w; + uint32_t y; + uint32_t z; + int k; + U d; + +#define d0 word0(&d) +#define d1 word1(&d) + + xa0 = a.words(); + xa = xa0 + a.size(); + y = *--xa; + ASSERT(y); + k = hi0bits(y); + *e = 32 - k; + if (k < Ebits) { + d0 = Exp_1 | (y >> (Ebits - k)); + w = xa > xa0 ? *--xa : 0; + d1 = (y << (32 - Ebits + k)) | (w >> (Ebits - k)); + goto returnD; + } + z = xa > xa0 ? *--xa : 0; + if (k -= Ebits) { + d0 = Exp_1 | (y << k) | (z >> (32 - k)); + y = xa > xa0 ? *--xa : 0; + d1 = (z << k) | (y >> (32 - k)); + } else { + d0 = Exp_1 | y; + d1 = z; + } +returnD: +#undef d0 +#undef d1 + return dval(&d); +} + +static ALWAYS_INLINE void d2b(BigInt& b, U* d, int* e, int* bits) +{ + int de, k; + uint32_t* x; + uint32_t y, z; + int i; +#define d0 word0(d) +#define d1 word1(d) + + b.sign = 0; + b.resize(1); + x = b.words(); + + z = d0 & Frac_mask; + d0 &= 0x7fffffff; /* clear sign bit, which we ignore */ + if ((de = (int)(d0 >> Exp_shift))) + z |= Exp_msk1; + if ((y = d1)) { + if ((k = lo0bits(&y))) { + x[0] = y | (z << (32 - k)); + z >>= k; + } else + x[0] = y; + if (z) { + b.resize(2); + x[1] = z; + } + + i = b.size(); + } else { + k = lo0bits(&z); + x[0] = z; + i = 1; + b.resize(1); + k += 32; + } + if (de) { + *e = de - Bias - (P - 1) + k; + *bits = P - k; + } else { + *e = 0 - Bias - (P - 1) + 1 + k; + *bits = (32 * i) - hi0bits(x[i - 1]); + } +} +#undef d0 +#undef d1 + +static double ratio(const BigInt& a, const BigInt& b) +{ + U da, db; + int k, ka, kb; + + dval(&da) = b2d(a, &ka); + dval(&db) = b2d(b, &kb); + k = ka - kb + 32 * (a.size() - b.size()); + if (k > 0) + word0(&da) += k * Exp_msk1; + else { + k = -k; + word0(&db) += k * Exp_msk1; + } + return dval(&da) / dval(&db); +} + +static const double tens[] = { + 1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9, + 1e10, 1e11, 1e12, 1e13, 1e14, 1e15, 1e16, 1e17, 1e18, 1e19, + 1e20, 1e21, 1e22 +}; + +static const double bigtens[] = { 1e16, 1e32, 1e64, 1e128, 1e256 }; +static const double tinytens[] = { 1e-16, 1e-32, 1e-64, 1e-128, + 9007199254740992. * 9007199254740992.e-256 + /* = 2^106 * 1e-256 */ +}; + +/* The factor of 2^53 in tinytens[4] helps us avoid setting the underflow */ +/* flag unnecessarily. It leads to a song and dance at the end of strtod. */ +#define Scale_Bit 0x10 +#define n_bigtens 5 + +double strtod(const char* s00, char** se) +{ + int scale; + int bb2, bb5, bbe, bd2, bd5, bbbits, bs2, c, dsign, + e, e1, esign, i, j, k, nd, nd0, nf, nz, nz0, sign; + const char *s, *s0, *s1; + double aadj, aadj1; + U aadj2, adj, rv, rv0; + int32_t L; + uint32_t y, z; + BigInt bb, bb1, bd, bd0, bs, delta; + + sign = nz0 = nz = 0; + dval(&rv) = 0; + for (s = s00; ; s++) { + switch (*s) { + case '-': + sign = 1; + /* no break */ + case '+': + if (*++s) + goto break2; + /* no break */ + case 0: + goto ret0; + case '\t': + case '\n': + case '\v': + case '\f': + case '\r': + case ' ': + continue; + default: + goto break2; + } + } +break2: + if (*s == '0') { + nz0 = 1; + while (*++s == '0') { } + if (!*s) + goto ret; + } + s0 = s; + y = z = 0; + for (nd = nf = 0; (c = *s) >= '0' && c <= '9'; nd++, s++) + if (nd < 9) + y = (10 * y) + c - '0'; + else if (nd < 16) + z = (10 * z) + c - '0'; + nd0 = nd; + if (c == '.') { + c = *++s; + if (!nd) { + for (; c == '0'; c = *++s) + nz++; + if (c > '0' && c <= '9') { + s0 = s; + nf += nz; + nz = 0; + goto haveDig; + } + goto digDone; + } + for (; c >= '0' && c <= '9'; c = *++s) { +haveDig: + nz++; + if (c -= '0') { + nf += nz; + for (i = 1; i < nz; i++) + if (nd++ < 9) + y *= 10; + else if (nd <= DBL_DIG + 1) + z *= 10; + if (nd++ < 9) + y = (10 * y) + c; + else if (nd <= DBL_DIG + 1) + z = (10 * z) + c; + nz = 0; + } + } + } +digDone: + e = 0; + if (c == 'e' || c == 'E') { + if (!nd && !nz && !nz0) + goto ret0; + s00 = s; + esign = 0; + switch (c = *++s) { + case '-': + esign = 1; + case '+': + c = *++s; + } + if (c >= '0' && c <= '9') { + while (c == '0') + c = *++s; + if (c > '0' && c <= '9') { + L = c - '0'; + s1 = s; + while ((c = *++s) >= '0' && c <= '9') + L = (10 * L) + c - '0'; + if (s - s1 > 8 || L > 19999) + /* Avoid confusion from exponents + * so large that e might overflow. + */ + e = 19999; /* safe for 16 bit ints */ + else + e = (int)L; + if (esign) + e = -e; + } else + e = 0; + } else + s = s00; + } + if (!nd) { + if (!nz && !nz0) { +ret0: + s = s00; + sign = 0; + } + goto ret; + } + e1 = e -= nf; + + /* Now we have nd0 digits, starting at s0, followed by a + * decimal point, followed by nd-nd0 digits. The number we're + * after is the integer represented by those digits times + * 10**e */ + + if (!nd0) + nd0 = nd; + k = nd < DBL_DIG + 1 ? nd : DBL_DIG + 1; + dval(&rv) = y; + if (k > 9) + dval(&rv) = tens[k - 9] * dval(&rv) + z; + if (nd <= DBL_DIG) { + if (!e) + goto ret; + if (e > 0) { + if (e <= Ten_pmax) { + /* rv = */ rounded_product(dval(&rv), tens[e]); + goto ret; + } + i = DBL_DIG - nd; + if (e <= Ten_pmax + i) { + /* A fancier test would sometimes let us do + * this for larger i values. + */ + e -= i; + dval(&rv) *= tens[i]; + /* rv = */ rounded_product(dval(&rv), tens[e]); + goto ret; + } + } else if (e >= -Ten_pmax) { + /* rv = */ rounded_quotient(dval(&rv), tens[-e]); + goto ret; + } + } + e1 += nd - k; + + scale = 0; + + /* Get starting approximation = rv * 10**e1 */ + + if (e1 > 0) { + if ((i = e1 & 15)) + dval(&rv) *= tens[i]; + if (e1 &= ~15) { + if (e1 > DBL_MAX_10_EXP) { +ovfl: +#if HAVE(ERRNO_H) + errno = ERANGE; +#endif + /* Can't trust HUGE_VAL */ + word0(&rv) = Exp_mask; + word1(&rv) = 0; + goto ret; + } + e1 >>= 4; + for (j = 0; e1 > 1; j++, e1 >>= 1) + if (e1 & 1) + dval(&rv) *= bigtens[j]; + /* The last multiplication could overflow. */ + word0(&rv) -= P * Exp_msk1; + dval(&rv) *= bigtens[j]; + if ((z = word0(&rv) & Exp_mask) > Exp_msk1 * (DBL_MAX_EXP + Bias - P)) + goto ovfl; + if (z > Exp_msk1 * (DBL_MAX_EXP + Bias - 1 - P)) { + /* set to largest number */ + /* (Can't trust DBL_MAX) */ + word0(&rv) = Big0; + word1(&rv) = Big1; + } else + word0(&rv) += P * Exp_msk1; + } + } else if (e1 < 0) { + e1 = -e1; + if ((i = e1 & 15)) + dval(&rv) /= tens[i]; + if (e1 >>= 4) { + if (e1 >= 1 << n_bigtens) + goto undfl; + if (e1 & Scale_Bit) + scale = 2 * P; + for (j = 0; e1 > 0; j++, e1 >>= 1) + if (e1 & 1) + dval(&rv) *= tinytens[j]; + if (scale && (j = (2 * P) + 1 - ((word0(&rv) & Exp_mask) >> Exp_shift)) > 0) { + /* scaled rv is denormal; clear j low bits */ + if (j >= 32) { + word1(&rv) = 0; + if (j >= 53) + word0(&rv) = (P + 2) * Exp_msk1; + else + word0(&rv) &= 0xffffffff << (j - 32); + } else + word1(&rv) &= 0xffffffff << j; + } + if (!dval(&rv)) { +undfl: + dval(&rv) = 0.; +#if HAVE(ERRNO_H) + errno = ERANGE; +#endif + goto ret; + } + } + } + + /* Now the hard part -- adjusting rv to the correct value.*/ + + /* Put digits into bd: true value = bd * 10^e */ + + s2b(bd0, s0, nd0, nd, y); + + for (;;) { + bd = bd0; + d2b(bb, &rv, &bbe, &bbbits); /* rv = bb * 2^bbe */ + i2b(bs, 1); + + if (e >= 0) { + bb2 = bb5 = 0; + bd2 = bd5 = e; + } else { + bb2 = bb5 = -e; + bd2 = bd5 = 0; + } + if (bbe >= 0) + bb2 += bbe; + else + bd2 -= bbe; + bs2 = bb2; + j = bbe - scale; + i = j + bbbits - 1; /* logb(rv) */ + if (i < Emin) /* denormal */ + j += P - Emin; + else + j = P + 1 - bbbits; + bb2 += j; + bd2 += j; + bd2 += scale; + i = bb2 < bd2 ? bb2 : bd2; + if (i > bs2) + i = bs2; + if (i > 0) { + bb2 -= i; + bd2 -= i; + bs2 -= i; + } + if (bb5 > 0) { + pow5mult(bs, bb5); + mult(bb, bs); + } + if (bb2 > 0) + lshift(bb, bb2); + if (bd5 > 0) + pow5mult(bd, bd5); + if (bd2 > 0) + lshift(bd, bd2); + if (bs2 > 0) + lshift(bs, bs2); + diff(delta, bb, bd); + dsign = delta.sign; + delta.sign = 0; + i = cmp(delta, bs); + + if (i < 0) { + /* Error is less than half an ulp -- check for + * special case of mantissa a power of two. + */ + if (dsign || word1(&rv) || word0(&rv) & Bndry_mask + || (word0(&rv) & Exp_mask) <= (2 * P + 1) * Exp_msk1 + ) { + break; + } + if (!delta.words()[0] && delta.size() <= 1) { + /* exact result */ + break; + } + lshift(delta, Log2P); + if (cmp(delta, bs) > 0) + goto dropDown; + break; + } + if (!i) { + /* exactly half-way between */ + if (dsign) { + if ((word0(&rv) & Bndry_mask1) == Bndry_mask1 + && word1(&rv) == ( + (scale && (y = word0(&rv) & Exp_mask) <= 2 * P * Exp_msk1) + ? (0xffffffff & (0xffffffff << (2 * P + 1 - (y >> Exp_shift)))) : + 0xffffffff)) { + /*boundary case -- increment exponent*/ + word0(&rv) = (word0(&rv) & Exp_mask) + Exp_msk1; + word1(&rv) = 0; + dsign = 0; + break; + } + } else if (!(word0(&rv) & Bndry_mask) && !word1(&rv)) { +dropDown: + /* boundary case -- decrement exponent */ + if (scale) { + L = word0(&rv) & Exp_mask; + if (L <= (2 * P + 1) * Exp_msk1) { + if (L > (P + 2) * Exp_msk1) + /* round even ==> */ + /* accept rv */ + break; + /* rv = smallest denormal */ + goto undfl; + } + } + L = (word0(&rv) & Exp_mask) - Exp_msk1; + word0(&rv) = L | Bndry_mask1; + word1(&rv) = 0xffffffff; + break; + } + if (!(word1(&rv) & LSB)) + break; + if (dsign) + dval(&rv) += ulp(&rv); + else { + dval(&rv) -= ulp(&rv); + if (!dval(&rv)) + goto undfl; + } + dsign = 1 - dsign; + break; + } + if ((aadj = ratio(delta, bs)) <= 2.) { + if (dsign) + aadj = aadj1 = 1.; + else if (word1(&rv) || word0(&rv) & Bndry_mask) { + if (word1(&rv) == Tiny1 && !word0(&rv)) + goto undfl; + aadj = 1.; + aadj1 = -1.; + } else { + /* special case -- power of FLT_RADIX to be */ + /* rounded down... */ + + if (aadj < 2. / FLT_RADIX) + aadj = 1. / FLT_RADIX; + else + aadj *= 0.5; + aadj1 = -aadj; + } + } else { + aadj *= 0.5; + aadj1 = dsign ? aadj : -aadj; + } + y = word0(&rv) & Exp_mask; + + /* Check for overflow */ + + if (y == Exp_msk1 * (DBL_MAX_EXP + Bias - 1)) { + dval(&rv0) = dval(&rv); + word0(&rv) -= P * Exp_msk1; + adj.d = aadj1 * ulp(&rv); + dval(&rv) += adj.d; + if ((word0(&rv) & Exp_mask) >= Exp_msk1 * (DBL_MAX_EXP + Bias - P)) { + if (word0(&rv0) == Big0 && word1(&rv0) == Big1) + goto ovfl; + word0(&rv) = Big0; + word1(&rv) = Big1; + goto cont; + } + word0(&rv) += P * Exp_msk1; + } else { + if (scale && y <= 2 * P * Exp_msk1) { + if (aadj <= 0x7fffffff) { + if ((z = (uint32_t)aadj) <= 0) + z = 1; + aadj = z; + aadj1 = dsign ? aadj : -aadj; + } + dval(&aadj2) = aadj1; + word0(&aadj2) += (2 * P + 1) * Exp_msk1 - y; + aadj1 = dval(&aadj2); + } + adj.d = aadj1 * ulp(&rv); + dval(&rv) += adj.d; + } + z = word0(&rv) & Exp_mask; + if (!scale && y == z) { + /* Can we stop now? */ + L = (int32_t)aadj; + aadj -= L; + /* The tolerances below are conservative. */ + if (dsign || word1(&rv) || word0(&rv) & Bndry_mask) { + if (aadj < .4999999 || aadj > .5000001) + break; + } else if (aadj < .4999999 / FLT_RADIX) + break; + } +cont: + {} + } + if (scale) { + word0(&rv0) = Exp_1 - 2 * P * Exp_msk1; + word1(&rv0) = 0; + dval(&rv) *= dval(&rv0); +#if HAVE(ERRNO_H) + /* try to avoid the bug of testing an 8087 register value */ + if (!word0(&rv) && !word1(&rv)) + errno = ERANGE; +#endif + } +ret: + if (se) + *se = const_cast<char*>(s); + return sign ? -dval(&rv) : dval(&rv); +} + +static ALWAYS_INLINE int quorem(BigInt& b, BigInt& S) +{ + size_t n; + uint32_t* bx; + uint32_t* bxe; + uint32_t q; + uint32_t* sx; + uint32_t* sxe; +#ifdef USE_LONG_LONG + unsigned long long borrow, carry, y, ys; +#else + uint32_t borrow, carry, y, ys; + uint32_t si, z, zs; +#endif + ASSERT(b.size() <= 1 || b.words()[b.size() - 1]); + ASSERT(S.size() <= 1 || S.words()[S.size() - 1]); + + n = S.size(); + ASSERT_WITH_MESSAGE(b.size() <= n, "oversize b in quorem"); + if (b.size() < n) + return 0; + sx = S.words(); + sxe = sx + --n; + bx = b.words(); + bxe = bx + n; + q = *bxe / (*sxe + 1); /* ensure q <= true quotient */ + ASSERT_WITH_MESSAGE(q <= 9, "oversized quotient in quorem"); + if (q) { + borrow = 0; + carry = 0; + do { +#ifdef USE_LONG_LONG + ys = *sx++ * (unsigned long long)q + carry; + carry = ys >> 32; + y = *bx - (ys & 0xffffffffUL) - borrow; + borrow = y >> 32 & (uint32_t)1; + *bx++ = (uint32_t)y & 0xffffffffUL; +#else + si = *sx++; + ys = (si & 0xffff) * q + carry; + zs = (si >> 16) * q + (ys >> 16); + carry = zs >> 16; + y = (*bx & 0xffff) - (ys & 0xffff) - borrow; + borrow = (y & 0x10000) >> 16; + z = (*bx >> 16) - (zs & 0xffff) - borrow; + borrow = (z & 0x10000) >> 16; + bx = storeInc(bx, z, y); +#endif + } while (sx <= sxe); + if (!*bxe) { + bx = b.words(); + while (--bxe > bx && !*bxe) + --n; + b.resize(n); + } + } + if (cmp(b, S) >= 0) { + q++; + borrow = 0; + carry = 0; + bx = b.words(); + sx = S.words(); + do { +#ifdef USE_LONG_LONG + ys = *sx++ + carry; + carry = ys >> 32; + y = *bx - (ys & 0xffffffffUL) - borrow; + borrow = y >> 32 & (uint32_t)1; + *bx++ = (uint32_t)y & 0xffffffffUL; +#else + si = *sx++; + ys = (si & 0xffff) + carry; + zs = (si >> 16) + (ys >> 16); + carry = zs >> 16; + y = (*bx & 0xffff) - (ys & 0xffff) - borrow; + borrow = (y & 0x10000) >> 16; + z = (*bx >> 16) - (zs & 0xffff) - borrow; + borrow = (z & 0x10000) >> 16; + bx = storeInc(bx, z, y); +#endif + } while (sx <= sxe); + bx = b.words(); + bxe = bx + n; + if (!*bxe) { + while (--bxe > bx && !*bxe) + --n; + b.resize(n); + } + } + return q; +} + +/* dtoa for IEEE arithmetic (dmg): convert double to ASCII string. + * + * Inspired by "How to Print Floating-Point Numbers Accurately" by + * Guy L. Steele, Jr. and Jon L. White [Proc. ACM SIGPLAN '90, pp. 112-126]. + * + * Modifications: + * 1. Rather than iterating, we use a simple numeric overestimate + * to determine k = floor(log10(d)). We scale relevant + * quantities using O(log2(k)) rather than O(k) multiplications. + * 2. For some modes > 2 (corresponding to ecvt and fcvt), we don't + * try to generate digits strictly left to right. Instead, we + * compute with fewer bits and propagate the carry if necessary + * when rounding the final digit up. This is often faster. + * 3. Under the assumption that input will be rounded nearest, + * mode 0 renders 1e23 as 1e23 rather than 9.999999999999999e22. + * That is, we allow equality in stopping tests when the + * round-nearest rule will give the same floating-point value + * as would satisfaction of the stopping test with strict + * inequality. + * 4. We remove common factors of powers of 2 from relevant + * quantities. + * 5. When converting floating-point integers less than 1e16, + * we use floating-point arithmetic rather than resorting + * to multiple-precision integers. + * 6. When asked to produce fewer than 15 digits, we first try + * to get by with floating-point arithmetic; we resort to + * multiple-precision integer arithmetic only if we cannot + * guarantee that the floating-point calculation has given + * the correctly rounded result. For k requested digits and + * "uniformly" distributed input, the probability is + * something like 10^(k-15) that we must resort to the int32_t + * calculation. + * + * Note: 'leftright' translates to 'generate shortest possible string'. + */ +template<bool roundingNone, bool roundingSignificantFigures, bool roundingDecimalPlaces, bool leftright> +void dtoa(DtoaBuffer result, double dd, int ndigits, bool& signOut, int& exponentOut, unsigned& precisionOut) +{ + // Exactly one rounding mode must be specified. + ASSERT(roundingNone + roundingSignificantFigures + roundingDecimalPlaces == 1); + // roundingNone only allowed (only sensible?) with leftright set. + ASSERT(!roundingNone || leftright); + + ASSERT(isfinite(dd)); + + int bbits, b2, b5, be, dig, i, ieps, ilim = 0, ilim0, ilim1 = 0, + j, j1, k, k0, k_check, m2, m5, s2, s5, + spec_case; + int32_t L; + int denorm; + uint32_t x; + BigInt b, delta, mlo, mhi, S; + U d2, eps, u; + double ds; + char* s; + char* s0; + + u.d = dd; + + /* Infinity or NaN */ + ASSERT((word0(&u) & Exp_mask) != Exp_mask); + + // JavaScript toString conversion treats -0 as 0. + if (!dval(&u)) { + signOut = false; + exponentOut = 0; + precisionOut = 1; + result[0] = '0'; + result[1] = '\0'; + return; + } + + if (word0(&u) & Sign_bit) { + signOut = true; + word0(&u) &= ~Sign_bit; // clear sign bit + } else + signOut = false; + + d2b(b, &u, &be, &bbits); + if ((i = (int)(word0(&u) >> Exp_shift1 & (Exp_mask >> Exp_shift1)))) { + dval(&d2) = dval(&u); + word0(&d2) &= Frac_mask1; + word0(&d2) |= Exp_11; + + /* log(x) ~=~ log(1.5) + (x-1.5)/1.5 + * log10(x) = log(x) / log(10) + * ~=~ log(1.5)/log(10) + (x-1.5)/(1.5*log(10)) + * log10(d) = (i-Bias)*log(2)/log(10) + log10(d2) + * + * This suggests computing an approximation k to log10(d) by + * + * k = (i - Bias)*0.301029995663981 + * + ( (d2-1.5)*0.289529654602168 + 0.176091259055681 ); + * + * We want k to be too large rather than too small. + * The error in the first-order Taylor series approximation + * is in our favor, so we just round up the constant enough + * to compensate for any error in the multiplication of + * (i - Bias) by 0.301029995663981; since |i - Bias| <= 1077, + * and 1077 * 0.30103 * 2^-52 ~=~ 7.2e-14, + * adding 1e-13 to the constant term more than suffices. + * Hence we adjust the constant term to 0.1760912590558. + * (We could get a more accurate k by invoking log10, + * but this is probably not worthwhile.) + */ + + i -= Bias; + denorm = 0; + } else { + /* d is denormalized */ + + i = bbits + be + (Bias + (P - 1) - 1); + x = (i > 32) ? (word0(&u) << (64 - i)) | (word1(&u) >> (i - 32)) + : word1(&u) << (32 - i); + dval(&d2) = x; + word0(&d2) -= 31 * Exp_msk1; /* adjust exponent */ + i -= (Bias + (P - 1) - 1) + 1; + denorm = 1; + } + ds = (dval(&d2) - 1.5) * 0.289529654602168 + 0.1760912590558 + (i * 0.301029995663981); + k = (int)ds; + if (ds < 0. && ds != k) + k--; /* want k = floor(ds) */ + k_check = 1; + if (k >= 0 && k <= Ten_pmax) { + if (dval(&u) < tens[k]) + k--; + k_check = 0; + } + j = bbits - i - 1; + if (j >= 0) { + b2 = 0; + s2 = j; + } else { + b2 = -j; + s2 = 0; + } + if (k >= 0) { + b5 = 0; + s5 = k; + s2 += k; + } else { + b2 -= k; + b5 = -k; + s5 = 0; + } + + if (roundingNone) { + ilim = ilim1 = -1; + i = 18; + ndigits = 0; + } + if (roundingSignificantFigures) { + if (ndigits <= 0) + ndigits = 1; + ilim = ilim1 = i = ndigits; + } + if (roundingDecimalPlaces) { + i = ndigits + k + 1; + ilim = i; + ilim1 = i - 1; + if (i <= 0) + i = 1; + } + + s = s0 = result; + + if (ilim >= 0 && ilim <= Quick_max) { + /* Try to get by with floating-point arithmetic. */ + + i = 0; + dval(&d2) = dval(&u); + k0 = k; + ilim0 = ilim; + ieps = 2; /* conservative */ + if (k > 0) { + ds = tens[k & 0xf]; + j = k >> 4; + if (j & Bletch) { + /* prevent overflows */ + j &= Bletch - 1; + dval(&u) /= bigtens[n_bigtens - 1]; + ieps++; + } + for (; j; j >>= 1, i++) { + if (j & 1) { + ieps++; + ds *= bigtens[i]; + } + } + dval(&u) /= ds; + } else if ((j1 = -k)) { + dval(&u) *= tens[j1 & 0xf]; + for (j = j1 >> 4; j; j >>= 1, i++) { + if (j & 1) { + ieps++; + dval(&u) *= bigtens[i]; + } + } + } + if (k_check && dval(&u) < 1. && ilim > 0) { + if (ilim1 <= 0) + goto fastFailed; + ilim = ilim1; + k--; + dval(&u) *= 10.; + ieps++; + } + dval(&eps) = (ieps * dval(&u)) + 7.; + word0(&eps) -= (P - 1) * Exp_msk1; + if (!ilim) { + S.clear(); + mhi.clear(); + dval(&u) -= 5.; + if (dval(&u) > dval(&eps)) + goto oneDigit; + if (dval(&u) < -dval(&eps)) + goto noDigits; + goto fastFailed; + } + if (leftright) { + /* Use Steele & White method of only + * generating digits needed. + */ + dval(&eps) = (0.5 / tens[ilim - 1]) - dval(&eps); + for (i = 0;;) { + L = (long int)dval(&u); + dval(&u) -= L; + *s++ = '0' + (int)L; + if (dval(&u) < dval(&eps)) + goto ret; + if (1. - dval(&u) < dval(&eps)) + goto bumpUp; + if (++i >= ilim) + break; + dval(&eps) *= 10.; + dval(&u) *= 10.; + } + } else { + /* Generate ilim digits, then fix them up. */ + dval(&eps) *= tens[ilim - 1]; + for (i = 1;; i++, dval(&u) *= 10.) { + L = (int32_t)(dval(&u)); + if (!(dval(&u) -= L)) + ilim = i; + *s++ = '0' + (int)L; + if (i == ilim) { + if (dval(&u) > 0.5 + dval(&eps)) + goto bumpUp; + if (dval(&u) < 0.5 - dval(&eps)) { + while (*--s == '0') { } + s++; + goto ret; + } + break; + } + } + } +fastFailed: + s = s0; + dval(&u) = dval(&d2); + k = k0; + ilim = ilim0; + } + + /* Do we have a "small" integer? */ + + if (be >= 0 && k <= Int_max) { + /* Yes. */ + ds = tens[k]; + if (ndigits < 0 && ilim <= 0) { + S.clear(); + mhi.clear(); + if (ilim < 0 || dval(&u) <= 5 * ds) + goto noDigits; + goto oneDigit; + } + for (i = 1;; i++, dval(&u) *= 10.) { + L = (int32_t)(dval(&u) / ds); + dval(&u) -= L * ds; + *s++ = '0' + (int)L; + if (!dval(&u)) { + break; + } + if (i == ilim) { + dval(&u) += dval(&u); + if (dval(&u) > ds || (dval(&u) == ds && (L & 1))) { +bumpUp: + while (*--s == '9') + if (s == s0) { + k++; + *s = '0'; + break; + } + ++*s++; + } + break; + } + } + goto ret; + } + + m2 = b2; + m5 = b5; + mhi.clear(); + mlo.clear(); + if (leftright) { + i = denorm ? be + (Bias + (P - 1) - 1 + 1) : 1 + P - bbits; + b2 += i; + s2 += i; + i2b(mhi, 1); + } + if (m2 > 0 && s2 > 0) { + i = m2 < s2 ? m2 : s2; + b2 -= i; + m2 -= i; + s2 -= i; + } + if (b5 > 0) { + if (leftright) { + if (m5 > 0) { + pow5mult(mhi, m5); + mult(b, mhi); + } + if ((j = b5 - m5)) + pow5mult(b, j); + } else + pow5mult(b, b5); + } + i2b(S, 1); + if (s5 > 0) + pow5mult(S, s5); + + /* Check for special case that d is a normalized power of 2. */ + + spec_case = 0; + if ((roundingNone || leftright) && (!word1(&u) && !(word0(&u) & Bndry_mask) && word0(&u) & (Exp_mask & ~Exp_msk1))) { + /* The special case */ + b2 += Log2P; + s2 += Log2P; + spec_case = 1; + } + + /* Arrange for convenient computation of quotients: + * shift left if necessary so divisor has 4 leading 0 bits. + * + * Perhaps we should just compute leading 28 bits of S once + * and for all and pass them and a shift to quorem, so it + * can do shifts and ors to compute the numerator for q. + */ + if ((i = ((s5 ? 32 - hi0bits(S.words()[S.size() - 1]) : 1) + s2) & 0x1f)) + i = 32 - i; + if (i > 4) { + i -= 4; + b2 += i; + m2 += i; + s2 += i; + } else if (i < 4) { + i += 28; + b2 += i; + m2 += i; + s2 += i; + } + if (b2 > 0) + lshift(b, b2); + if (s2 > 0) + lshift(S, s2); + if (k_check) { + if (cmp(b, S) < 0) { + k--; + multadd(b, 10, 0); /* we botched the k estimate */ + if (leftright) + multadd(mhi, 10, 0); + ilim = ilim1; + } + } + if (ilim <= 0 && roundingDecimalPlaces) { + if (ilim < 0) + goto noDigits; + multadd(S, 5, 0); + // For IEEE-754 unbiased rounding this check should be <=, such that 0.5 would flush to zero. + if (cmp(b, S) < 0) + goto noDigits; + goto oneDigit; + } + if (leftright) { + if (m2 > 0) + lshift(mhi, m2); + + /* Compute mlo -- check for special case + * that d is a normalized power of 2. + */ + + mlo = mhi; + if (spec_case) + lshift(mhi, Log2P); + + for (i = 1;;i++) { + dig = quorem(b, S) + '0'; + /* Do we yet have the shortest decimal string + * that will round to d? + */ + j = cmp(b, mlo); + diff(delta, S, mhi); + j1 = delta.sign ? 1 : cmp(b, delta); +#ifdef DTOA_ROUND_BIASED + if (j < 0 || !j) { +#else + // FIXME: ECMA-262 specifies that equidistant results round away from + // zero, which probably means we shouldn't be on the unbiased code path + // (the (word1(&u) & 1) clause is looking highly suspicious). I haven't + // yet understood this code well enough to make the call, but we should + // probably be enabling DTOA_ROUND_BIASED. I think the interesting corner + // case to understand is probably "Math.pow(0.5, 24).toString()". + // I believe this value is interesting because I think it is precisely + // representable in binary floating point, and its decimal representation + // has a single digit that Steele & White reduction can remove, with the + // value 5 (thus equidistant from the next numbers above and below). + // We produce the correct answer using either codepath, and I don't as + // yet understand why. :-) + if (!j1 && !(word1(&u) & 1)) { + if (dig == '9') + goto round9up; + if (j > 0) + dig++; + *s++ = dig; + goto ret; + } + if (j < 0 || (!j && !(word1(&u) & 1))) { +#endif + if ((b.words()[0] || b.size() > 1) && (j1 > 0)) { + lshift(b, 1); + j1 = cmp(b, S); + // For IEEE-754 round-to-even, this check should be (j1 > 0 || (!j1 && (dig & 1))), + // but ECMA-262 specifies that equidistant values (e.g. (.5).toFixed()) should + // be rounded away from zero. + if (j1 >= 0) { + if (dig == '9') + goto round9up; + dig++; + } + } + *s++ = dig; + goto ret; + } + if (j1 > 0) { + if (dig == '9') { /* possible if i == 1 */ +round9up: + *s++ = '9'; + goto roundoff; + } + *s++ = dig + 1; + goto ret; + } + *s++ = dig; + if (i == ilim) + break; + multadd(b, 10, 0); + multadd(mlo, 10, 0); + multadd(mhi, 10, 0); + } + } else { + for (i = 1;; i++) { + *s++ = dig = quorem(b, S) + '0'; + if (!b.words()[0] && b.size() <= 1) + goto ret; + if (i >= ilim) + break; + multadd(b, 10, 0); + } + } + + /* Round off last digit */ + + lshift(b, 1); + j = cmp(b, S); + // For IEEE-754 round-to-even, this check should be (j > 0 || (!j && (dig & 1))), + // but ECMA-262 specifies that equidistant values (e.g. (.5).toFixed()) should + // be rounded away from zero. + if (j >= 0) { +roundoff: + while (*--s == '9') + if (s == s0) { + k++; + *s++ = '1'; + goto ret; + } + ++*s++; + } else { + while (*--s == '0') { } + s++; + } + goto ret; +noDigits: + exponentOut = 0; + precisionOut = 1; + result[0] = '0'; + result[1] = '\0'; + return; +oneDigit: + *s++ = '1'; + k++; + goto ret; +ret: + ASSERT(s > result); + *s = 0; + exponentOut = k; + precisionOut = s - result; +} + +void dtoa(DtoaBuffer result, double dd, bool& sign, int& exponent, unsigned& precision) +{ + // flags are roundingNone, leftright. + dtoa<true, false, false, true>(result, dd, 0, sign, exponent, precision); +} + +void dtoaRoundSF(DtoaBuffer result, double dd, int ndigits, bool& sign, int& exponent, unsigned& precision) +{ + // flag is roundingSignificantFigures. + dtoa<false, true, false, false>(result, dd, ndigits, sign, exponent, precision); +} + +void dtoaRoundDP(DtoaBuffer result, double dd, int ndigits, bool& sign, int& exponent, unsigned& precision) +{ + // flag is roundingDecimalPlaces. + dtoa<false, false, true, false>(result, dd, ndigits, sign, exponent, precision); +} + +const char* numberToString(double d, NumberToStringBuffer buffer) +{ + double_conversion::StringBuilder builder(buffer, NumberToStringBufferLength); + const double_conversion::DoubleToStringConverter& converter = double_conversion::DoubleToStringConverter::EcmaScriptConverter(); + converter.ToShortest(d, &builder); + return builder.Finalize(); +} + +static inline const char* formatStringTruncatingTrailingZerosIfNeeded(NumberToStringBuffer buffer, double_conversion::StringBuilder& builder) +{ + size_t length = builder.position(); + size_t decimalPointPosition = 0; + for (; decimalPointPosition < length; ++decimalPointPosition) { + if (buffer[decimalPointPosition] == '.') + break; + } + + // No decimal seperator found, early exit. + if (decimalPointPosition == length) + return builder.Finalize(); + + size_t truncatedLength = length - 1; + for (; truncatedLength > decimalPointPosition; --truncatedLength) { + if (buffer[truncatedLength] != '0') + break; + } + + // No trailing zeros found to strip. + if (truncatedLength == length - 1) + return builder.Finalize(); + + // If we removed all trailing zeros, remove the decimal point as well. + if (truncatedLength == decimalPointPosition) { + ASSERT(truncatedLength > 0); + --truncatedLength; + } + + // Truncate the StringBuilder, and return the final result. + builder.SetPosition(truncatedLength + 1); + return builder.Finalize(); +} + +const char* numberToFixedPrecisionString(double d, unsigned significantFigures, NumberToStringBuffer buffer, bool truncateTrailingZeros) +{ + // Mimic String::format("%.[precision]g", ...), but use dtoas rounding facilities. + // "g": Signed value printed in f or e format, whichever is more compact for the given value and precision. + // The e format is used only when the exponent of the value is less than –4 or greater than or equal to the + // precision argument. Trailing zeros are truncated, and the decimal point appears only if one or more digits follow it. + // "precision": The precision specifies the maximum number of significant digits printed. + double_conversion::StringBuilder builder(buffer, NumberToStringBufferLength); + const double_conversion::DoubleToStringConverter& converter = double_conversion::DoubleToStringConverter::EcmaScriptConverter(); + converter.ToPrecision(d, significantFigures, &builder); + if (!truncateTrailingZeros) + return builder.Finalize(); + return formatStringTruncatingTrailingZerosIfNeeded(buffer, builder); +} + +const char* numberToFixedWidthString(double d, unsigned decimalPlaces, NumberToStringBuffer buffer) +{ + // Mimic String::format("%.[precision]f", ...), but use dtoas rounding facilities. + // "f": Signed value having the form [ – ]dddd.dddd, where dddd is one or more decimal digits. + // The number of digits before the decimal point depends on the magnitude of the number, and + // the number of digits after the decimal point depends on the requested precision. + // "precision": The precision value specifies the number of digits after the decimal point. + // If a decimal point appears, at least one digit appears before it. + // The value is rounded to the appropriate number of digits. + double_conversion::StringBuilder builder(buffer, NumberToStringBufferLength); + const double_conversion::DoubleToStringConverter& converter = double_conversion::DoubleToStringConverter::EcmaScriptConverter(); + converter.ToFixed(d, decimalPlaces, &builder); + return builder.Finalize(); +} + +} // namespace WTF diff --git a/Source/JavaScriptCore/wtf/dtoa.h b/Source/JavaScriptCore/wtf/dtoa.h new file mode 100644 index 000000000..df33e2cdc --- /dev/null +++ b/Source/JavaScriptCore/wtf/dtoa.h @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2003, 2008 Apple Inc. All rights reserved. + * + * This library 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 library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#ifndef WTF_dtoa_h +#define WTF_dtoa_h + +#include <wtf/dtoa/double-conversion.h> +#include <wtf/unicode/Unicode.h> + +namespace WTF { +class Mutex; + +extern WTF::Mutex* s_dtoaP5Mutex; + +typedef char DtoaBuffer[80]; + +void dtoa(DtoaBuffer result, double dd, bool& sign, int& exponent, unsigned& precision); +void dtoaRoundSF(DtoaBuffer result, double dd, int ndigits, bool& sign, int& exponent, unsigned& precision); +void dtoaRoundDP(DtoaBuffer result, double dd, int ndigits, bool& sign, int& exponent, unsigned& precision); + +// s00: input string. Must not be 0 and must be terminated by 0. +// se: *se will have the last consumed character position + 1. +double strtod(const char* s00, char** se); + +// Size = 80 for sizeof(DtoaBuffer) + some sign bits, decimal point, 'e', exponent digits. +const unsigned NumberToStringBufferLength = 96; +typedef char NumberToStringBuffer[NumberToStringBufferLength]; +typedef UChar NumberToUStringBuffer[NumberToStringBufferLength]; +const char* numberToString(double, NumberToStringBuffer); +const char* numberToFixedPrecisionString(double, unsigned significantFigures, NumberToStringBuffer, bool truncateTrailingZeros = false); +const char* numberToFixedWidthString(double, unsigned decimalPlaces, NumberToStringBuffer); + +} // namespace WTF + +using WTF::NumberToStringBuffer; +using WTF::NumberToUStringBuffer; +using WTF::numberToString; +using WTF::numberToFixedPrecisionString; +using WTF::numberToFixedWidthString; + +#endif // WTF_dtoa_h diff --git a/Source/JavaScriptCore/wtf/dtoa/COPYING b/Source/JavaScriptCore/wtf/dtoa/COPYING new file mode 100644 index 000000000..933718a9e --- /dev/null +++ b/Source/JavaScriptCore/wtf/dtoa/COPYING @@ -0,0 +1,26 @@ +Copyright 2006-2011, the V8 project authors. 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 Google Inc. 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. diff --git a/Source/JavaScriptCore/wtf/dtoa/LICENSE b/Source/JavaScriptCore/wtf/dtoa/LICENSE new file mode 100644 index 000000000..933718a9e --- /dev/null +++ b/Source/JavaScriptCore/wtf/dtoa/LICENSE @@ -0,0 +1,26 @@ +Copyright 2006-2011, the V8 project authors. 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 Google Inc. 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. diff --git a/Source/JavaScriptCore/wtf/dtoa/README b/Source/JavaScriptCore/wtf/dtoa/README new file mode 100644 index 000000000..f186b420f --- /dev/null +++ b/Source/JavaScriptCore/wtf/dtoa/README @@ -0,0 +1,11 @@ +http://code.google.com/p/double-conversion + +This project (double-conversion) provides binary-decimal and decimal-binary +routines for IEEE doubles. + +The library consists of efficient conversion routines that have been extracted +from the V8 JavaScript engine. The code has been refactored and improved so that +it can be used more easily in other projects. + +There is extensive documentation in src/double-conversion.h. Other examples can +be found in test/cctest/test-conversions.cc. diff --git a/Source/JavaScriptCore/wtf/dtoa/bignum-dtoa.cc b/Source/JavaScriptCore/wtf/dtoa/bignum-dtoa.cc new file mode 100644 index 000000000..38be56c73 --- /dev/null +++ b/Source/JavaScriptCore/wtf/dtoa/bignum-dtoa.cc @@ -0,0 +1,659 @@ +// Copyright 2010 the V8 project authors. 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 Google Inc. 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 "config.h" + +#include <math.h> + +#include "bignum-dtoa.h" + +#include "bignum.h" +#include "double.h" + +namespace WTF { + +namespace double_conversion { + + static int NormalizedExponent(uint64_t significand, int exponent) { + ASSERT(significand != 0); + while ((significand & Double::kHiddenBit) == 0) { + significand = significand << 1; + exponent = exponent - 1; + } + return exponent; + } + + + // Forward declarations: + // Returns an estimation of k such that 10^(k-1) <= v < 10^k. + static int EstimatePower(int exponent); + // Computes v / 10^estimated_power exactly, as a ratio of two bignums, numerator + // and denominator. + static void InitialScaledStartValues(double v, + int estimated_power, + bool need_boundary_deltas, + Bignum* numerator, + Bignum* denominator, + Bignum* delta_minus, + Bignum* delta_plus); + // Multiplies numerator/denominator so that its values lies in the range 1-10. + // Returns decimal_point s.t. + // v = numerator'/denominator' * 10^(decimal_point-1) + // where numerator' and denominator' are the values of numerator and + // denominator after the call to this function. + static void FixupMultiply10(int estimated_power, bool is_even, + int* decimal_point, + Bignum* numerator, Bignum* denominator, + Bignum* delta_minus, Bignum* delta_plus); + // Generates digits from the left to the right and stops when the generated + // digits yield the shortest decimal representation of v. + static void GenerateShortestDigits(Bignum* numerator, Bignum* denominator, + Bignum* delta_minus, Bignum* delta_plus, + bool is_even, + Vector<char> buffer, int* length); + // Generates 'requested_digits' after the decimal point. + static void BignumToFixed(int requested_digits, int* decimal_point, + Bignum* numerator, Bignum* denominator, + Vector<char>(buffer), int* length); + // Generates 'count' digits of numerator/denominator. + // Once 'count' digits have been produced rounds the result depending on the + // remainder (remainders of exactly .5 round upwards). Might update the + // decimal_point when rounding up (for example for 0.9999). + static void GenerateCountedDigits(int count, int* decimal_point, + Bignum* numerator, Bignum* denominator, + Vector<char>(buffer), int* length); + + + void BignumDtoa(double v, BignumDtoaMode mode, int requested_digits, + Vector<char> buffer, int* length, int* decimal_point) { + ASSERT(v > 0); + ASSERT(!Double(v).IsSpecial()); + uint64_t significand = Double(v).Significand(); + bool is_even = (significand & 1) == 0; + int exponent = Double(v).Exponent(); + int normalized_exponent = NormalizedExponent(significand, exponent); + // estimated_power might be too low by 1. + int estimated_power = EstimatePower(normalized_exponent); + + // Shortcut for Fixed. + // The requested digits correspond to the digits after the point. If the + // number is much too small, then there is no need in trying to get any + // digits. + if (mode == BIGNUM_DTOA_FIXED && -estimated_power - 1 > requested_digits) { + buffer[0] = '\0'; + *length = 0; + // Set decimal-point to -requested_digits. This is what Gay does. + // Note that it should not have any effect anyways since the string is + // empty. + *decimal_point = -requested_digits; + return; + } + + Bignum numerator; + Bignum denominator; + Bignum delta_minus; + Bignum delta_plus; + // Make sure the bignum can grow large enough. The smallest double equals + // 4e-324. In this case the denominator needs fewer than 324*4 binary digits. + // The maximum double is 1.7976931348623157e308 which needs fewer than + // 308*4 binary digits. + ASSERT(Bignum::kMaxSignificantBits >= 324*4); + bool need_boundary_deltas = (mode == BIGNUM_DTOA_SHORTEST); + InitialScaledStartValues(v, estimated_power, need_boundary_deltas, + &numerator, &denominator, + &delta_minus, &delta_plus); + // We now have v = (numerator / denominator) * 10^estimated_power. + FixupMultiply10(estimated_power, is_even, decimal_point, + &numerator, &denominator, + &delta_minus, &delta_plus); + // We now have v = (numerator / denominator) * 10^(decimal_point-1), and + // 1 <= (numerator + delta_plus) / denominator < 10 + switch (mode) { + case BIGNUM_DTOA_SHORTEST: + GenerateShortestDigits(&numerator, &denominator, + &delta_minus, &delta_plus, + is_even, buffer, length); + break; + case BIGNUM_DTOA_FIXED: + BignumToFixed(requested_digits, decimal_point, + &numerator, &denominator, + buffer, length); + break; + case BIGNUM_DTOA_PRECISION: + GenerateCountedDigits(requested_digits, decimal_point, + &numerator, &denominator, + buffer, length); + break; + default: + UNREACHABLE(); + } + buffer[*length] = '\0'; + } + + + // The procedure starts generating digits from the left to the right and stops + // when the generated digits yield the shortest decimal representation of v. A + // decimal representation of v is a number lying closer to v than to any other + // double, so it converts to v when read. + // + // This is true if d, the decimal representation, is between m- and m+, the + // upper and lower boundaries. d must be strictly between them if !is_even. + // m- := (numerator - delta_minus) / denominator + // m+ := (numerator + delta_plus) / denominator + // + // Precondition: 0 <= (numerator+delta_plus) / denominator < 10. + // If 1 <= (numerator+delta_plus) / denominator < 10 then no leading 0 digit + // will be produced. This should be the standard precondition. + static void GenerateShortestDigits(Bignum* numerator, Bignum* denominator, + Bignum* delta_minus, Bignum* delta_plus, + bool is_even, + Vector<char> buffer, int* length) { + // Small optimization: if delta_minus and delta_plus are the same just reuse + // one of the two bignums. + if (Bignum::Equal(*delta_minus, *delta_plus)) { + delta_plus = delta_minus; + } + *length = 0; + while (true) { + uint16_t digit; + digit = numerator->DivideModuloIntBignum(*denominator); + ASSERT(digit <= 9); // digit is a uint16_t and therefore always positive. + // digit = numerator / denominator (integer division). + // numerator = numerator % denominator. + buffer[(*length)++] = digit + '0'; + + // Can we stop already? + // If the remainder of the division is less than the distance to the lower + // boundary we can stop. In this case we simply round down (discarding the + // remainder). + // Similarly we test if we can round up (using the upper boundary). + bool in_delta_room_minus; + bool in_delta_room_plus; + if (is_even) { + in_delta_room_minus = Bignum::LessEqual(*numerator, *delta_minus); + } else { + in_delta_room_minus = Bignum::Less(*numerator, *delta_minus); + } + if (is_even) { + in_delta_room_plus = + Bignum::PlusCompare(*numerator, *delta_plus, *denominator) >= 0; + } else { + in_delta_room_plus = + Bignum::PlusCompare(*numerator, *delta_plus, *denominator) > 0; + } + if (!in_delta_room_minus && !in_delta_room_plus) { + // Prepare for next iteration. + numerator->Times10(); + delta_minus->Times10(); + // We optimized delta_plus to be equal to delta_minus (if they share the + // same value). So don't multiply delta_plus if they point to the same + // object. + if (delta_minus != delta_plus) { + delta_plus->Times10(); + } + } else if (in_delta_room_minus && in_delta_room_plus) { + // Let's see if 2*numerator < denominator. + // If yes, then the next digit would be < 5 and we can round down. + int compare = Bignum::PlusCompare(*numerator, *numerator, *denominator); + if (compare < 0) { + // Remaining digits are less than .5. -> Round down (== do nothing). + } else if (compare > 0) { + // Remaining digits are more than .5 of denominator. -> Round up. + // Note that the last digit could not be a '9' as otherwise the whole + // loop would have stopped earlier. + // We still have an assert here in case the preconditions were not + // satisfied. + ASSERT(buffer[(*length) - 1] != '9'); + buffer[(*length) - 1]++; + } else { + // Halfway case. + // TODO(floitsch): need a way to solve half-way cases. + // For now let's round towards even (since this is what Gay seems to + // do). + + if ((buffer[(*length) - 1] - '0') % 2 == 0) { + // Round down => Do nothing. + } else { + ASSERT(buffer[(*length) - 1] != '9'); + buffer[(*length) - 1]++; + } + } + return; + } else if (in_delta_room_minus) { + // Round down (== do nothing). + return; + } else { // in_delta_room_plus + // Round up. + // Note again that the last digit could not be '9' since this would have + // stopped the loop earlier. + // We still have an ASSERT here, in case the preconditions were not + // satisfied. + ASSERT(buffer[(*length) -1] != '9'); + buffer[(*length) - 1]++; + return; + } + } + } + + + // Let v = numerator / denominator < 10. + // Then we generate 'count' digits of d = x.xxxxx... (without the decimal point) + // from left to right. Once 'count' digits have been produced we decide wether + // to round up or down. Remainders of exactly .5 round upwards. Numbers such + // as 9.999999 propagate a carry all the way, and change the + // exponent (decimal_point), when rounding upwards. + static void GenerateCountedDigits(int count, int* decimal_point, + Bignum* numerator, Bignum* denominator, + Vector<char>(buffer), int* length) { + ASSERT(count >= 0); + for (int i = 0; i < count - 1; ++i) { + uint16_t digit; + digit = numerator->DivideModuloIntBignum(*denominator); + ASSERT(digit <= 9); // digit is a uint16_t and therefore always positive. + // digit = numerator / denominator (integer division). + // numerator = numerator % denominator. + buffer[i] = digit + '0'; + // Prepare for next iteration. + numerator->Times10(); + } + // Generate the last digit. + uint16_t digit; + digit = numerator->DivideModuloIntBignum(*denominator); + if (Bignum::PlusCompare(*numerator, *numerator, *denominator) >= 0) { + digit++; + } + buffer[count - 1] = digit + '0'; + // Correct bad digits (in case we had a sequence of '9's). Propagate the + // carry until we hat a non-'9' or til we reach the first digit. + for (int i = count - 1; i > 0; --i) { + if (buffer[i] != '0' + 10) break; + buffer[i] = '0'; + buffer[i - 1]++; + } + if (buffer[0] == '0' + 10) { + // Propagate a carry past the top place. + buffer[0] = '1'; + (*decimal_point)++; + } + *length = count; + } + + + // Generates 'requested_digits' after the decimal point. It might omit + // trailing '0's. If the input number is too small then no digits at all are + // generated (ex.: 2 fixed digits for 0.00001). + // + // Input verifies: 1 <= (numerator + delta) / denominator < 10. + static void BignumToFixed(int requested_digits, int* decimal_point, + Bignum* numerator, Bignum* denominator, + Vector<char>(buffer), int* length) { + // Note that we have to look at more than just the requested_digits, since + // a number could be rounded up. Example: v=0.5 with requested_digits=0. + // Even though the power of v equals 0 we can't just stop here. + if (-(*decimal_point) > requested_digits) { + // The number is definitively too small. + // Ex: 0.001 with requested_digits == 1. + // Set decimal-point to -requested_digits. This is what Gay does. + // Note that it should not have any effect anyways since the string is + // empty. + *decimal_point = -requested_digits; + *length = 0; + return; + } else if (-(*decimal_point) == requested_digits) { + // We only need to verify if the number rounds down or up. + // Ex: 0.04 and 0.06 with requested_digits == 1. + ASSERT(*decimal_point == -requested_digits); + // Initially the fraction lies in range (1, 10]. Multiply the denominator + // by 10 so that we can compare more easily. + denominator->Times10(); + if (Bignum::PlusCompare(*numerator, *numerator, *denominator) >= 0) { + // If the fraction is >= 0.5 then we have to include the rounded + // digit. + buffer[0] = '1'; + *length = 1; + (*decimal_point)++; + } else { + // Note that we caught most of similar cases earlier. + *length = 0; + } + return; + } else { + // The requested digits correspond to the digits after the point. + // The variable 'needed_digits' includes the digits before the point. + int needed_digits = (*decimal_point) + requested_digits; + GenerateCountedDigits(needed_digits, decimal_point, + numerator, denominator, + buffer, length); + } + } + + + // Returns an estimation of k such that 10^(k-1) <= v < 10^k where + // v = f * 2^exponent and 2^52 <= f < 2^53. + // v is hence a normalized double with the given exponent. The output is an + // approximation for the exponent of the decimal approimation .digits * 10^k. + // + // The result might undershoot by 1 in which case 10^k <= v < 10^k+1. + // Note: this property holds for v's upper boundary m+ too. + // 10^k <= m+ < 10^k+1. + // (see explanation below). + // + // Examples: + // EstimatePower(0) => 16 + // EstimatePower(-52) => 0 + // + // Note: e >= 0 => EstimatedPower(e) > 0. No similar claim can be made for e<0. + static int EstimatePower(int exponent) { + // This function estimates log10 of v where v = f*2^e (with e == exponent). + // Note that 10^floor(log10(v)) <= v, but v <= 10^ceil(log10(v)). + // Note that f is bounded by its container size. Let p = 53 (the double's + // significand size). Then 2^(p-1) <= f < 2^p. + // + // Given that log10(v) == log2(v)/log2(10) and e+(len(f)-1) is quite close + // to log2(v) the function is simplified to (e+(len(f)-1)/log2(10)). + // The computed number undershoots by less than 0.631 (when we compute log3 + // and not log10). + // + // Optimization: since we only need an approximated result this computation + // can be performed on 64 bit integers. On x86/x64 architecture the speedup is + // not really measurable, though. + // + // Since we want to avoid overshooting we decrement by 1e10 so that + // floating-point imprecisions don't affect us. + // + // Explanation for v's boundary m+: the computation takes advantage of + // the fact that 2^(p-1) <= f < 2^p. Boundaries still satisfy this requirement + // (even for denormals where the delta can be much more important). + + const double k1Log10 = 0.30102999566398114; // 1/lg(10) + + // For doubles len(f) == 53 (don't forget the hidden bit). + const int kSignificandSize = 53; + double estimate = ceil((exponent + kSignificandSize - 1) * k1Log10 - 1e-10); + return static_cast<int>(estimate); + } + + + // See comments for InitialScaledStartValues. + static void InitialScaledStartValuesPositiveExponent( + double v, int estimated_power, bool need_boundary_deltas, + Bignum* numerator, Bignum* denominator, + Bignum* delta_minus, Bignum* delta_plus) { + // A positive exponent implies a positive power. + ASSERT(estimated_power >= 0); + // Since the estimated_power is positive we simply multiply the denominator + // by 10^estimated_power. + + // numerator = v. + numerator->AssignUInt64(Double(v).Significand()); + numerator->ShiftLeft(Double(v).Exponent()); + // denominator = 10^estimated_power. + denominator->AssignPowerUInt16(10, estimated_power); + + if (need_boundary_deltas) { + // Introduce a common denominator so that the deltas to the boundaries are + // integers. + denominator->ShiftLeft(1); + numerator->ShiftLeft(1); + // Let v = f * 2^e, then m+ - v = 1/2 * 2^e; With the common + // denominator (of 2) delta_plus equals 2^e. + delta_plus->AssignUInt16(1); + delta_plus->ShiftLeft(Double(v).Exponent()); + // Same for delta_minus (with adjustments below if f == 2^p-1). + delta_minus->AssignUInt16(1); + delta_minus->ShiftLeft(Double(v).Exponent()); + + // If the significand (without the hidden bit) is 0, then the lower + // boundary is closer than just half a ulp (unit in the last place). + // There is only one exception: if the next lower number is a denormal then + // the distance is 1 ulp. This cannot be the case for exponent >= 0 (but we + // have to test it in the other function where exponent < 0). + uint64_t v_bits = Double(v).AsUint64(); + if ((v_bits & Double::kSignificandMask) == 0) { + // The lower boundary is closer at half the distance of "normal" numbers. + // Increase the common denominator and adapt all but the delta_minus. + denominator->ShiftLeft(1); // *2 + numerator->ShiftLeft(1); // *2 + delta_plus->ShiftLeft(1); // *2 + } + } + } + + + // See comments for InitialScaledStartValues + static void InitialScaledStartValuesNegativeExponentPositivePower( + double v, int estimated_power, bool need_boundary_deltas, + Bignum* numerator, Bignum* denominator, + Bignum* delta_minus, Bignum* delta_plus) { + uint64_t significand = Double(v).Significand(); + int exponent = Double(v).Exponent(); + // v = f * 2^e with e < 0, and with estimated_power >= 0. + // This means that e is close to 0 (have a look at how estimated_power is + // computed). + + // numerator = significand + // since v = significand * 2^exponent this is equivalent to + // numerator = v * / 2^-exponent + numerator->AssignUInt64(significand); + // denominator = 10^estimated_power * 2^-exponent (with exponent < 0) + denominator->AssignPowerUInt16(10, estimated_power); + denominator->ShiftLeft(-exponent); + + if (need_boundary_deltas) { + // Introduce a common denominator so that the deltas to the boundaries are + // integers. + denominator->ShiftLeft(1); + numerator->ShiftLeft(1); + // Let v = f * 2^e, then m+ - v = 1/2 * 2^e; With the common + // denominator (of 2) delta_plus equals 2^e. + // Given that the denominator already includes v's exponent the distance + // to the boundaries is simply 1. + delta_plus->AssignUInt16(1); + // Same for delta_minus (with adjustments below if f == 2^p-1). + delta_minus->AssignUInt16(1); + + // If the significand (without the hidden bit) is 0, then the lower + // boundary is closer than just one ulp (unit in the last place). + // There is only one exception: if the next lower number is a denormal + // then the distance is 1 ulp. Since the exponent is close to zero + // (otherwise estimated_power would have been negative) this cannot happen + // here either. + uint64_t v_bits = Double(v).AsUint64(); + if ((v_bits & Double::kSignificandMask) == 0) { + // The lower boundary is closer at half the distance of "normal" numbers. + // Increase the denominator and adapt all but the delta_minus. + denominator->ShiftLeft(1); // *2 + numerator->ShiftLeft(1); // *2 + delta_plus->ShiftLeft(1); // *2 + } + } + } + + + // See comments for InitialScaledStartValues + static void InitialScaledStartValuesNegativeExponentNegativePower( + double v, int estimated_power, bool need_boundary_deltas, + Bignum* numerator, Bignum* denominator, + Bignum* delta_minus, Bignum* delta_plus) { + const uint64_t kMinimalNormalizedExponent = + UINT64_2PART_C(0x00100000, 00000000); + uint64_t significand = Double(v).Significand(); + int exponent = Double(v).Exponent(); + // Instead of multiplying the denominator with 10^estimated_power we + // multiply all values (numerator and deltas) by 10^-estimated_power. + + // Use numerator as temporary container for power_ten. + Bignum* power_ten = numerator; + power_ten->AssignPowerUInt16(10, -estimated_power); + + if (need_boundary_deltas) { + // Since power_ten == numerator we must make a copy of 10^estimated_power + // before we complete the computation of the numerator. + // delta_plus = delta_minus = 10^estimated_power + delta_plus->AssignBignum(*power_ten); + delta_minus->AssignBignum(*power_ten); + } + + // numerator = significand * 2 * 10^-estimated_power + // since v = significand * 2^exponent this is equivalent to + // numerator = v * 10^-estimated_power * 2 * 2^-exponent. + // Remember: numerator has been abused as power_ten. So no need to assign it + // to itself. + ASSERT(numerator == power_ten); + numerator->MultiplyByUInt64(significand); + + // denominator = 2 * 2^-exponent with exponent < 0. + denominator->AssignUInt16(1); + denominator->ShiftLeft(-exponent); + + if (need_boundary_deltas) { + // Introduce a common denominator so that the deltas to the boundaries are + // integers. + numerator->ShiftLeft(1); + denominator->ShiftLeft(1); + // With this shift the boundaries have their correct value, since + // delta_plus = 10^-estimated_power, and + // delta_minus = 10^-estimated_power. + // These assignments have been done earlier. + + // The special case where the lower boundary is twice as close. + // This time we have to look out for the exception too. + uint64_t v_bits = Double(v).AsUint64(); + if ((v_bits & Double::kSignificandMask) == 0 && + // The only exception where a significand == 0 has its boundaries at + // "normal" distances: + (v_bits & Double::kExponentMask) != kMinimalNormalizedExponent) { + numerator->ShiftLeft(1); // *2 + denominator->ShiftLeft(1); // *2 + delta_plus->ShiftLeft(1); // *2 + } + } + } + + + // Let v = significand * 2^exponent. + // Computes v / 10^estimated_power exactly, as a ratio of two bignums, numerator + // and denominator. The functions GenerateShortestDigits and + // GenerateCountedDigits will then convert this ratio to its decimal + // representation d, with the required accuracy. + // Then d * 10^estimated_power is the representation of v. + // (Note: the fraction and the estimated_power might get adjusted before + // generating the decimal representation.) + // + // The initial start values consist of: + // - a scaled numerator: s.t. numerator/denominator == v / 10^estimated_power. + // - a scaled (common) denominator. + // optionally (used by GenerateShortestDigits to decide if it has the shortest + // decimal converting back to v): + // - v - m-: the distance to the lower boundary. + // - m+ - v: the distance to the upper boundary. + // + // v, m+, m-, and therefore v - m- and m+ - v all share the same denominator. + // + // Let ep == estimated_power, then the returned values will satisfy: + // v / 10^ep = numerator / denominator. + // v's boundarys m- and m+: + // m- / 10^ep == v / 10^ep - delta_minus / denominator + // m+ / 10^ep == v / 10^ep + delta_plus / denominator + // Or in other words: + // m- == v - delta_minus * 10^ep / denominator; + // m+ == v + delta_plus * 10^ep / denominator; + // + // Since 10^(k-1) <= v < 10^k (with k == estimated_power) + // or 10^k <= v < 10^(k+1) + // we then have 0.1 <= numerator/denominator < 1 + // or 1 <= numerator/denominator < 10 + // + // It is then easy to kickstart the digit-generation routine. + // + // The boundary-deltas are only filled if need_boundary_deltas is set. + static void InitialScaledStartValues(double v, + int estimated_power, + bool need_boundary_deltas, + Bignum* numerator, + Bignum* denominator, + Bignum* delta_minus, + Bignum* delta_plus) { + if (Double(v).Exponent() >= 0) { + InitialScaledStartValuesPositiveExponent( + v, estimated_power, need_boundary_deltas, + numerator, denominator, delta_minus, delta_plus); + } else if (estimated_power >= 0) { + InitialScaledStartValuesNegativeExponentPositivePower( + v, estimated_power, need_boundary_deltas, + numerator, denominator, delta_minus, delta_plus); + } else { + InitialScaledStartValuesNegativeExponentNegativePower( + v, estimated_power, need_boundary_deltas, + numerator, denominator, delta_minus, delta_plus); + } + } + + + // This routine multiplies numerator/denominator so that its values lies in the + // range 1-10. That is after a call to this function we have: + // 1 <= (numerator + delta_plus) /denominator < 10. + // Let numerator the input before modification and numerator' the argument + // after modification, then the output-parameter decimal_point is such that + // numerator / denominator * 10^estimated_power == + // numerator' / denominator' * 10^(decimal_point - 1) + // In some cases estimated_power was too low, and this is already the case. We + // then simply adjust the power so that 10^(k-1) <= v < 10^k (with k == + // estimated_power) but do not touch the numerator or denominator. + // Otherwise the routine multiplies the numerator and the deltas by 10. + static void FixupMultiply10(int estimated_power, bool is_even, + int* decimal_point, + Bignum* numerator, Bignum* denominator, + Bignum* delta_minus, Bignum* delta_plus) { + bool in_range; + if (is_even) { + // For IEEE doubles half-way cases (in decimal system numbers ending with 5) + // are rounded to the closest floating-point number with even significand. + in_range = Bignum::PlusCompare(*numerator, *delta_plus, *denominator) >= 0; + } else { + in_range = Bignum::PlusCompare(*numerator, *delta_plus, *denominator) > 0; + } + if (in_range) { + // Since numerator + delta_plus >= denominator we already have + // 1 <= numerator/denominator < 10. Simply update the estimated_power. + *decimal_point = estimated_power + 1; + } else { + *decimal_point = estimated_power; + numerator->Times10(); + if (Bignum::Equal(*delta_minus, *delta_plus)) { + delta_minus->Times10(); + delta_plus->AssignBignum(*delta_minus); + } else { + delta_minus->Times10(); + delta_plus->Times10(); + } + } + } + +} // namespace double_conversion + +} // namespace WTF diff --git a/Source/JavaScriptCore/wtf/dtoa/bignum-dtoa.h b/Source/JavaScriptCore/wtf/dtoa/bignum-dtoa.h new file mode 100644 index 000000000..076168709 --- /dev/null +++ b/Source/JavaScriptCore/wtf/dtoa/bignum-dtoa.h @@ -0,0 +1,86 @@ +// Copyright 2010 the V8 project authors. 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 Google Inc. 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. + +#ifndef DOUBLE_CONVERSION_BIGNUM_DTOA_H_ +#define DOUBLE_CONVERSION_BIGNUM_DTOA_H_ + +#include "utils.h" + +namespace WTF { + +namespace double_conversion { + + enum BignumDtoaMode { + // Return the shortest correct representation. + // For example the output of 0.299999999999999988897 is (the less accurate but + // correct) 0.3. + BIGNUM_DTOA_SHORTEST, + // Return a fixed number of digits after the decimal point. + // For instance fixed(0.1, 4) becomes 0.1000 + // If the input number is big, the output will be big. + BIGNUM_DTOA_FIXED, + // Return a fixed number of digits, no matter what the exponent is. + BIGNUM_DTOA_PRECISION + }; + + // Converts the given double 'v' to ascii. + // The result should be interpreted as buffer * 10^(point-length). + // The buffer will be null-terminated. + // + // The input v must be > 0 and different from NaN, and Infinity. + // + // The output depends on the given mode: + // - SHORTEST: produce the least amount of digits for which the internal + // identity requirement is still satisfied. If the digits are printed + // (together with the correct exponent) then reading this number will give + // 'v' again. The buffer will choose the representation that is closest to + // 'v'. If there are two at the same distance, than the number is round up. + // In this mode the 'requested_digits' parameter is ignored. + // - FIXED: produces digits necessary to print a given number with + // 'requested_digits' digits after the decimal point. The produced digits + // might be too short in which case the caller has to fill the gaps with '0's. + // Example: toFixed(0.001, 5) is allowed to return buffer="1", point=-2. + // Halfway cases are rounded up. The call toFixed(0.15, 2) thus returns + // buffer="2", point=0. + // Note: the length of the returned buffer has no meaning wrt the significance + // of its digits. That is, just because it contains '0's does not mean that + // any other digit would not satisfy the internal identity requirement. + // - PRECISION: produces 'requested_digits' where the first digit is not '0'. + // Even though the length of produced digits usually equals + // 'requested_digits', the function is allowed to return fewer digits, in + // which case the caller has to fill the missing digits with '0's. + // Halfway cases are again rounded up. + // 'BignumDtoa' expects the given buffer to be big enough to hold all digits + // and a terminating null-character. + void BignumDtoa(double v, BignumDtoaMode mode, int requested_digits, + Vector<char> buffer, int* length, int* point); + +} // namespace double_conversion + +} // namespace WTF + +#endif // DOUBLE_CONVERSION_BIGNUM_DTOA_H_ diff --git a/Source/JavaScriptCore/wtf/dtoa/bignum.cc b/Source/JavaScriptCore/wtf/dtoa/bignum.cc new file mode 100644 index 000000000..46a900a85 --- /dev/null +++ b/Source/JavaScriptCore/wtf/dtoa/bignum.cc @@ -0,0 +1,770 @@ +// Copyright 2010 the V8 project authors. 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 Google Inc. 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 "config.h" + +#include "bignum.h" +#include "utils.h" + +namespace WTF { + +namespace double_conversion { + + Bignum::Bignum() + : bigits_(bigits_buffer_, kBigitCapacity), used_digits_(0), exponent_(0) { + for (int i = 0; i < kBigitCapacity; ++i) { + bigits_[i] = 0; + } + } + + + template<typename S> + static int BitSize(S value) { + return 8 * sizeof(value); + } + + // Guaranteed to lie in one Bigit. + void Bignum::AssignUInt16(uint16_t value) { + ASSERT(kBigitSize >= BitSize(value)); + Zero(); + if (value == 0) return; + + EnsureCapacity(1); + bigits_[0] = value; + used_digits_ = 1; + } + + + void Bignum::AssignUInt64(uint64_t value) { + const int kUInt64Size = 64; + + Zero(); + if (value == 0) return; + + int needed_bigits = kUInt64Size / kBigitSize + 1; + EnsureCapacity(needed_bigits); + for (int i = 0; i < needed_bigits; ++i) { + bigits_[i] = (uint32_t)value & kBigitMask; + value = value >> kBigitSize; + } + used_digits_ = needed_bigits; + Clamp(); + } + + + void Bignum::AssignBignum(const Bignum& other) { + exponent_ = other.exponent_; + for (int i = 0; i < other.used_digits_; ++i) { + bigits_[i] = other.bigits_[i]; + } + // Clear the excess digits (if there were any). + for (int i = other.used_digits_; i < used_digits_; ++i) { + bigits_[i] = 0; + } + used_digits_ = other.used_digits_; + } + + + static uint64_t ReadUInt64(Vector<const char> buffer, + int from, + int digits_to_read) { + uint64_t result = 0; + for (int i = from; i < from + digits_to_read; ++i) { + int digit = buffer[i] - '0'; + ASSERT(0 <= digit && digit <= 9); + result = result * 10 + digit; + } + return result; + } + + + void Bignum::AssignDecimalString(Vector<const char> value) { + // 2^64 = 18446744073709551616 > 10^19 + const int kMaxUint64DecimalDigits = 19; + Zero(); + int length = value.length(); + int pos = 0; + // Let's just say that each digit needs 4 bits. + while (length >= kMaxUint64DecimalDigits) { + uint64_t digits = ReadUInt64(value, pos, kMaxUint64DecimalDigits); + pos += kMaxUint64DecimalDigits; + length -= kMaxUint64DecimalDigits; + MultiplyByPowerOfTen(kMaxUint64DecimalDigits); + AddUInt64(digits); + } + uint64_t digits = ReadUInt64(value, pos, length); + MultiplyByPowerOfTen(length); + AddUInt64(digits); + Clamp(); + } + + + static int HexCharValue(char c) { + if ('0' <= c && c <= '9') return c - '0'; + if ('a' <= c && c <= 'f') return 10 + c - 'a'; + if ('A' <= c && c <= 'F') return 10 + c - 'A'; + UNREACHABLE(); + return 0; // To make compiler happy. + } + + + void Bignum::AssignHexString(Vector<const char> value) { + Zero(); + int length = value.length(); + + int needed_bigits = length * 4 / kBigitSize + 1; + EnsureCapacity(needed_bigits); + int string_index = length - 1; + for (int i = 0; i < needed_bigits - 1; ++i) { + // These bigits are guaranteed to be "full". + Chunk current_bigit = 0; + for (int j = 0; j < kBigitSize / 4; j++) { + current_bigit += HexCharValue(value[string_index--]) << (j * 4); + } + bigits_[i] = current_bigit; + } + used_digits_ = needed_bigits - 1; + + Chunk most_significant_bigit = 0; // Could be = 0; + for (int j = 0; j <= string_index; ++j) { + most_significant_bigit <<= 4; + most_significant_bigit += HexCharValue(value[j]); + } + if (most_significant_bigit != 0) { + bigits_[used_digits_] = most_significant_bigit; + used_digits_++; + } + Clamp(); + } + + + void Bignum::AddUInt64(uint64_t operand) { + if (operand == 0) return; + Bignum other; + other.AssignUInt64(operand); + AddBignum(other); + } + + + void Bignum::AddBignum(const Bignum& other) { + ASSERT(IsClamped()); + ASSERT(other.IsClamped()); + + // If this has a greater exponent than other append zero-bigits to this. + // After this call exponent_ <= other.exponent_. + Align(other); + + // There are two possibilities: + // aaaaaaaaaaa 0000 (where the 0s represent a's exponent) + // bbbbb 00000000 + // ---------------- + // ccccccccccc 0000 + // or + // aaaaaaaaaa 0000 + // bbbbbbbbb 0000000 + // ----------------- + // cccccccccccc 0000 + // In both cases we might need a carry bigit. + + EnsureCapacity(1 + Max(BigitLength(), other.BigitLength()) - exponent_); + Chunk carry = 0; + int bigit_pos = other.exponent_ - exponent_; + ASSERT(bigit_pos >= 0); + for (int i = 0; i < other.used_digits_; ++i) { + Chunk sum = bigits_[bigit_pos] + other.bigits_[i] + carry; + bigits_[bigit_pos] = sum & kBigitMask; + carry = sum >> kBigitSize; + bigit_pos++; + } + + while (carry != 0) { + Chunk sum = bigits_[bigit_pos] + carry; + bigits_[bigit_pos] = sum & kBigitMask; + carry = sum >> kBigitSize; + bigit_pos++; + } + used_digits_ = Max(bigit_pos, used_digits_); + ASSERT(IsClamped()); + } + + + void Bignum::SubtractBignum(const Bignum& other) { + ASSERT(IsClamped()); + ASSERT(other.IsClamped()); + // We require this to be bigger than other. + ASSERT(LessEqual(other, *this)); + + Align(other); + + int offset = other.exponent_ - exponent_; + Chunk borrow = 0; + int i; + for (i = 0; i < other.used_digits_; ++i) { + ASSERT((borrow == 0) || (borrow == 1)); + Chunk difference = bigits_[i + offset] - other.bigits_[i] - borrow; + bigits_[i + offset] = difference & kBigitMask; + borrow = difference >> (kChunkSize - 1); + } + while (borrow != 0) { + Chunk difference = bigits_[i + offset] - borrow; + bigits_[i + offset] = difference & kBigitMask; + borrow = difference >> (kChunkSize - 1); + ++i; + } + Clamp(); + } + + + void Bignum::ShiftLeft(int shift_amount) { + if (used_digits_ == 0) return; + exponent_ += shift_amount / kBigitSize; + int local_shift = shift_amount % kBigitSize; + EnsureCapacity(used_digits_ + 1); + BigitsShiftLeft(local_shift); + } + + + void Bignum::MultiplyByUInt32(uint32_t factor) { + if (factor == 1) return; + if (factor == 0) { + Zero(); + return; + } + if (used_digits_ == 0) return; + + // The product of a bigit with the factor is of size kBigitSize + 32. + // Assert that this number + 1 (for the carry) fits into double chunk. + ASSERT(kDoubleChunkSize >= kBigitSize + 32 + 1); + DoubleChunk carry = 0; + for (int i = 0; i < used_digits_; ++i) { + DoubleChunk product = static_cast<DoubleChunk>(factor) * bigits_[i] + carry; + bigits_[i] = static_cast<Chunk>(product & kBigitMask); + carry = (product >> kBigitSize); + } + while (carry != 0) { + EnsureCapacity(used_digits_ + 1); + bigits_[used_digits_] = (uint32_t)carry & kBigitMask; + used_digits_++; + carry >>= kBigitSize; + } + } + + + void Bignum::MultiplyByUInt64(uint64_t factor) { + if (factor == 1) return; + if (factor == 0) { + Zero(); + return; + } + ASSERT(kBigitSize < 32); + uint64_t carry = 0; + uint64_t low = factor & 0xFFFFFFFF; + uint64_t high = factor >> 32; + for (int i = 0; i < used_digits_; ++i) { + uint64_t product_low = low * bigits_[i]; + uint64_t product_high = high * bigits_[i]; + uint64_t tmp = (carry & kBigitMask) + product_low; + bigits_[i] = (uint32_t)tmp & kBigitMask; + carry = (carry >> kBigitSize) + (tmp >> kBigitSize) + + (product_high << (32 - kBigitSize)); + } + while (carry != 0) { + EnsureCapacity(used_digits_ + 1); + bigits_[used_digits_] = (uint32_t)carry & kBigitMask; + used_digits_++; + carry >>= kBigitSize; + } + } + + + void Bignum::MultiplyByPowerOfTen(int exponent) { + const uint64_t kFive27 = UINT64_2PART_C(0x6765c793, fa10079d); + const uint16_t kFive1 = 5; + const uint16_t kFive2 = kFive1 * 5; + const uint16_t kFive3 = kFive2 * 5; + const uint16_t kFive4 = kFive3 * 5; + const uint16_t kFive5 = kFive4 * 5; + const uint16_t kFive6 = kFive5 * 5; + const uint32_t kFive7 = kFive6 * 5; + const uint32_t kFive8 = kFive7 * 5; + const uint32_t kFive9 = kFive8 * 5; + const uint32_t kFive10 = kFive9 * 5; + const uint32_t kFive11 = kFive10 * 5; + const uint32_t kFive12 = kFive11 * 5; + const uint32_t kFive13 = kFive12 * 5; + const uint32_t kFive1_to_12[] = + { kFive1, kFive2, kFive3, kFive4, kFive5, kFive6, + kFive7, kFive8, kFive9, kFive10, kFive11, kFive12 }; + + ASSERT(exponent >= 0); + if (exponent == 0) return; + if (used_digits_ == 0) return; + + // We shift by exponent at the end just before returning. + int remaining_exponent = exponent; + while (remaining_exponent >= 27) { + MultiplyByUInt64(kFive27); + remaining_exponent -= 27; + } + while (remaining_exponent >= 13) { + MultiplyByUInt32(kFive13); + remaining_exponent -= 13; + } + if (remaining_exponent > 0) { + MultiplyByUInt32(kFive1_to_12[remaining_exponent - 1]); + } + ShiftLeft(exponent); + } + + + void Bignum::Square() { + ASSERT(IsClamped()); + int product_length = 2 * used_digits_; + EnsureCapacity(product_length); + + // Comba multiplication: compute each column separately. + // Example: r = a2a1a0 * b2b1b0. + // r = 1 * a0b0 + + // 10 * (a1b0 + a0b1) + + // 100 * (a2b0 + a1b1 + a0b2) + + // 1000 * (a2b1 + a1b2) + + // 10000 * a2b2 + // + // In the worst case we have to accumulate nb-digits products of digit*digit. + // + // Assert that the additional number of bits in a DoubleChunk are enough to + // sum up used_digits of Bigit*Bigit. + if ((1 << (2 * (kChunkSize - kBigitSize))) <= used_digits_) { + UNIMPLEMENTED(); + } + DoubleChunk accumulator = 0; + // First shift the digits so we don't overwrite them. + int copy_offset = used_digits_; + for (int i = 0; i < used_digits_; ++i) { + bigits_[copy_offset + i] = bigits_[i]; + } + // We have two loops to avoid some 'if's in the loop. + for (int i = 0; i < used_digits_; ++i) { + // Process temporary digit i with power i. + // The sum of the two indices must be equal to i. + int bigit_index1 = i; + int bigit_index2 = 0; + // Sum all of the sub-products. + while (bigit_index1 >= 0) { + Chunk chunk1 = bigits_[copy_offset + bigit_index1]; + Chunk chunk2 = bigits_[copy_offset + bigit_index2]; + accumulator += static_cast<DoubleChunk>(chunk1) * chunk2; + bigit_index1--; + bigit_index2++; + } + bigits_[i] = static_cast<Chunk>(accumulator) & kBigitMask; + accumulator >>= kBigitSize; + } + for (int i = used_digits_; i < product_length; ++i) { + int bigit_index1 = used_digits_ - 1; + int bigit_index2 = i - bigit_index1; + // Invariant: sum of both indices is again equal to i. + // Inner loop runs 0 times on last iteration, emptying accumulator. + while (bigit_index2 < used_digits_) { + Chunk chunk1 = bigits_[copy_offset + bigit_index1]; + Chunk chunk2 = bigits_[copy_offset + bigit_index2]; + accumulator += static_cast<DoubleChunk>(chunk1) * chunk2; + bigit_index1--; + bigit_index2++; + } + // The overwritten bigits_[i] will never be read in further loop iterations, + // because bigit_index1 and bigit_index2 are always greater + // than i - used_digits_. + bigits_[i] = static_cast<Chunk>(accumulator) & kBigitMask; + accumulator >>= kBigitSize; + } + // Since the result was guaranteed to lie inside the number the + // accumulator must be 0 now. + ASSERT(accumulator == 0); + + // Don't forget to update the used_digits and the exponent. + used_digits_ = product_length; + exponent_ *= 2; + Clamp(); + } + + + void Bignum::AssignPowerUInt16(uint16_t base, int power_exponent) { + ASSERT(base != 0); + ASSERT(power_exponent >= 0); + if (power_exponent == 0) { + AssignUInt16(1); + return; + } + Zero(); + int shifts = 0; + // We expect base to be in range 2-32, and most often to be 10. + // It does not make much sense to implement different algorithms for counting + // the bits. + while ((base & 1) == 0) { + base >>= 1; + shifts++; + } + int bit_size = 0; + int tmp_base = base; + while (tmp_base != 0) { + tmp_base >>= 1; + bit_size++; + } + int final_size = bit_size * power_exponent; + // 1 extra bigit for the shifting, and one for rounded final_size. + EnsureCapacity(final_size / kBigitSize + 2); + + // Left to Right exponentiation. + int mask = 1; + while (power_exponent >= mask) mask <<= 1; + + // The mask is now pointing to the bit above the most significant 1-bit of + // power_exponent. + // Get rid of first 1-bit; + mask >>= 2; + uint64_t this_value = base; + + bool delayed_multipliciation = false; + const uint64_t max_32bits = 0xFFFFFFFF; + while (mask != 0 && this_value <= max_32bits) { + this_value = this_value * this_value; + // Verify that there is enough space in this_value to perform the + // multiplication. The first bit_size bits must be 0. + if ((power_exponent & mask) != 0) { + uint64_t base_bits_mask = + ~((static_cast<uint64_t>(1) << (64 - bit_size)) - 1); + bool high_bits_zero = (this_value & base_bits_mask) == 0; + if (high_bits_zero) { + this_value *= base; + } else { + delayed_multipliciation = true; + } + } + mask >>= 1; + } + AssignUInt64(this_value); + if (delayed_multipliciation) { + MultiplyByUInt32(base); + } + + // Now do the same thing as a bignum. + while (mask != 0) { + Square(); + if ((power_exponent & mask) != 0) { + MultiplyByUInt32(base); + } + mask >>= 1; + } + + // And finally add the saved shifts. + ShiftLeft(shifts * power_exponent); + } + + + // Precondition: this/other < 16bit. + uint16_t Bignum::DivideModuloIntBignum(const Bignum& other) { + ASSERT(IsClamped()); + ASSERT(other.IsClamped()); + ASSERT(other.used_digits_ > 0); + + // Easy case: if we have less digits than the divisor than the result is 0. + // Note: this handles the case where this == 0, too. + if (BigitLength() < other.BigitLength()) { + return 0; + } + + Align(other); + + uint16_t result = 0; + + // Start by removing multiples of 'other' until both numbers have the same + // number of digits. + while (BigitLength() > other.BigitLength()) { + // This naive approach is extremely inefficient if the this divided other + // might be big. This function is implemented for doubleToString where + // the result should be small (less than 10). + ASSERT(other.bigits_[other.used_digits_ - 1] >= ((1 << kBigitSize) / 16)); + // Remove the multiples of the first digit. + // Example this = 23 and other equals 9. -> Remove 2 multiples. + result += bigits_[used_digits_ - 1]; + SubtractTimes(other, bigits_[used_digits_ - 1]); + } + + ASSERT(BigitLength() == other.BigitLength()); + + // Both bignums are at the same length now. + // Since other has more than 0 digits we know that the access to + // bigits_[used_digits_ - 1] is safe. + Chunk this_bigit = bigits_[used_digits_ - 1]; + Chunk other_bigit = other.bigits_[other.used_digits_ - 1]; + + if (other.used_digits_ == 1) { + // Shortcut for easy (and common) case. + int quotient = this_bigit / other_bigit; + bigits_[used_digits_ - 1] = this_bigit - other_bigit * quotient; + result += quotient; + Clamp(); + return result; + } + + int division_estimate = this_bigit / (other_bigit + 1); + result += division_estimate; + SubtractTimes(other, division_estimate); + + if (other_bigit * (division_estimate + 1) > this_bigit) { + // No need to even try to subtract. Even if other's remaining digits were 0 + // another subtraction would be too much. + return result; + } + + while (LessEqual(other, *this)) { + SubtractBignum(other); + result++; + } + return result; + } + + + template<typename S> + static int SizeInHexChars(S number) { + ASSERT(number > 0); + int result = 0; + while (number != 0) { + number >>= 4; + result++; + } + return result; + } + + + static char HexCharOfValue(int value) { + ASSERT(0 <= value && value <= 16); + if (value < 10) return value + '0'; + return value - 10 + 'A'; + } + + + bool Bignum::ToHexString(char* buffer, int buffer_size) const { + ASSERT(IsClamped()); + // Each bigit must be printable as separate hex-character. + ASSERT(kBigitSize % 4 == 0); + const int kHexCharsPerBigit = kBigitSize / 4; + + if (used_digits_ == 0) { + if (buffer_size < 2) return false; + buffer[0] = '0'; + buffer[1] = '\0'; + return true; + } + // We add 1 for the terminating '\0' character. + int needed_chars = (BigitLength() - 1) * kHexCharsPerBigit + + SizeInHexChars(bigits_[used_digits_ - 1]) + 1; + if (needed_chars > buffer_size) return false; + int string_index = needed_chars - 1; + buffer[string_index--] = '\0'; + for (int i = 0; i < exponent_; ++i) { + for (int j = 0; j < kHexCharsPerBigit; ++j) { + buffer[string_index--] = '0'; + } + } + for (int i = 0; i < used_digits_ - 1; ++i) { + Chunk current_bigit = bigits_[i]; + for (int j = 0; j < kHexCharsPerBigit; ++j) { + buffer[string_index--] = HexCharOfValue(current_bigit & 0xF); + current_bigit >>= 4; + } + } + // And finally the last bigit. + Chunk most_significant_bigit = bigits_[used_digits_ - 1]; + while (most_significant_bigit != 0) { + buffer[string_index--] = HexCharOfValue(most_significant_bigit & 0xF); + most_significant_bigit >>= 4; + } + return true; + } + + + Bignum::Chunk Bignum::BigitAt(int index) const { + if (index >= BigitLength()) return 0; + if (index < exponent_) return 0; + return bigits_[index - exponent_]; + } + + + int Bignum::Compare(const Bignum& a, const Bignum& b) { + ASSERT(a.IsClamped()); + ASSERT(b.IsClamped()); + int bigit_length_a = a.BigitLength(); + int bigit_length_b = b.BigitLength(); + if (bigit_length_a < bigit_length_b) return -1; + if (bigit_length_a > bigit_length_b) return +1; + for (int i = bigit_length_a - 1; i >= Min(a.exponent_, b.exponent_); --i) { + Chunk bigit_a = a.BigitAt(i); + Chunk bigit_b = b.BigitAt(i); + if (bigit_a < bigit_b) return -1; + if (bigit_a > bigit_b) return +1; + // Otherwise they are equal up to this digit. Try the next digit. + } + return 0; + } + + + int Bignum::PlusCompare(const Bignum& a, const Bignum& b, const Bignum& c) { + ASSERT(a.IsClamped()); + ASSERT(b.IsClamped()); + ASSERT(c.IsClamped()); + if (a.BigitLength() < b.BigitLength()) { + return PlusCompare(b, a, c); + } + if (a.BigitLength() + 1 < c.BigitLength()) return -1; + if (a.BigitLength() > c.BigitLength()) return +1; + // The exponent encodes 0-bigits. So if there are more 0-digits in 'a' than + // 'b' has digits, then the bigit-length of 'a'+'b' must be equal to the one + // of 'a'. + if (a.exponent_ >= b.BigitLength() && a.BigitLength() < c.BigitLength()) { + return -1; + } + + Chunk borrow = 0; + // Starting at min_exponent all digits are == 0. So no need to compare them. + int min_exponent = Min(Min(a.exponent_, b.exponent_), c.exponent_); + for (int i = c.BigitLength() - 1; i >= min_exponent; --i) { + Chunk chunk_a = a.BigitAt(i); + Chunk chunk_b = b.BigitAt(i); + Chunk chunk_c = c.BigitAt(i); + Chunk sum = chunk_a + chunk_b; + if (sum > chunk_c + borrow) { + return +1; + } else { + borrow = chunk_c + borrow - sum; + if (borrow > 1) return -1; + borrow <<= kBigitSize; + } + } + if (borrow == 0) return 0; + return -1; + } + + + void Bignum::Clamp() { + while (used_digits_ > 0 && bigits_[used_digits_ - 1] == 0) { + used_digits_--; + } + if (used_digits_ == 0) { + // Zero. + exponent_ = 0; + } + } + + + bool Bignum::IsClamped() const { + return used_digits_ == 0 || bigits_[used_digits_ - 1] != 0; + } + + + void Bignum::Zero() { + for (int i = 0; i < used_digits_; ++i) { + bigits_[i] = 0; + } + used_digits_ = 0; + exponent_ = 0; + } + + + void Bignum::Align(const Bignum& other) { + if (exponent_ > other.exponent_) { + // If "X" represents a "hidden" digit (by the exponent) then we are in the + // following case (a == this, b == other): + // a: aaaaaaXXXX or a: aaaaaXXX + // b: bbbbbbX b: bbbbbbbbXX + // We replace some of the hidden digits (X) of a with 0 digits. + // a: aaaaaa000X or a: aaaaa0XX + int zero_digits = exponent_ - other.exponent_; + EnsureCapacity(used_digits_ + zero_digits); + for (int i = used_digits_ - 1; i >= 0; --i) { + bigits_[i + zero_digits] = bigits_[i]; + } + for (int i = 0; i < zero_digits; ++i) { + bigits_[i] = 0; + } + used_digits_ += zero_digits; + exponent_ -= zero_digits; + ASSERT(used_digits_ >= 0); + ASSERT(exponent_ >= 0); + } + } + + + void Bignum::BigitsShiftLeft(int shift_amount) { + ASSERT(shift_amount < kBigitSize); + ASSERT(shift_amount >= 0); + Chunk carry = 0; + for (int i = 0; i < used_digits_; ++i) { + Chunk new_carry = bigits_[i] >> (kBigitSize - shift_amount); + bigits_[i] = ((bigits_[i] << shift_amount) + carry) & kBigitMask; + carry = new_carry; + } + if (carry != 0) { + bigits_[used_digits_] = carry; + used_digits_++; + } + } + + + void Bignum::SubtractTimes(const Bignum& other, int factor) { + ASSERT(exponent_ <= other.exponent_); + if (factor < 3) { + for (int i = 0; i < factor; ++i) { + SubtractBignum(other); + } + return; + } + Chunk borrow = 0; + int exponent_diff = other.exponent_ - exponent_; + for (int i = 0; i < other.used_digits_; ++i) { + DoubleChunk product = static_cast<DoubleChunk>(factor) * other.bigits_[i]; + DoubleChunk remove = borrow + product; + Chunk difference = bigits_[i + exponent_diff] - ((uint32_t)remove & kBigitMask); + bigits_[i + exponent_diff] = difference & kBigitMask; + borrow = static_cast<Chunk>((difference >> (kChunkSize - 1)) + + (remove >> kBigitSize)); + } + for (int i = other.used_digits_ + exponent_diff; i < used_digits_; ++i) { + if (borrow == 0) return; + Chunk difference = bigits_[i] - borrow; + bigits_[i] = difference & kBigitMask; + borrow = difference >> (kChunkSize - 1); + ++i; + } + Clamp(); + } + + +} // namespace double_conversion + +} // namespace WTF diff --git a/Source/JavaScriptCore/wtf/dtoa/bignum.h b/Source/JavaScriptCore/wtf/dtoa/bignum.h new file mode 100644 index 000000000..1a750581a --- /dev/null +++ b/Source/JavaScriptCore/wtf/dtoa/bignum.h @@ -0,0 +1,145 @@ +// Copyright 2010 the V8 project authors. 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 Google Inc. 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. + +#ifndef DOUBLE_CONVERSION_BIGNUM_H_ +#define DOUBLE_CONVERSION_BIGNUM_H_ + +#include "utils.h" + +namespace WTF { + +namespace double_conversion { + + class Bignum { + public: + // 3584 = 128 * 28. We can represent 2^3584 > 10^1000 accurately. + // This bignum can encode much bigger numbers, since it contains an + // exponent. + static const int kMaxSignificantBits = 3584; + + Bignum(); + void AssignUInt16(uint16_t value); + void AssignUInt64(uint64_t value); + void AssignBignum(const Bignum& other); + + void AssignDecimalString(Vector<const char> value); + void AssignHexString(Vector<const char> value); + + void AssignPowerUInt16(uint16_t base, int exponent); + + void AddUInt16(uint16_t operand); + void AddUInt64(uint64_t operand); + void AddBignum(const Bignum& other); + // Precondition: this >= other. + void SubtractBignum(const Bignum& other); + + void Square(); + void ShiftLeft(int shift_amount); + void MultiplyByUInt32(uint32_t factor); + void MultiplyByUInt64(uint64_t factor); + void MultiplyByPowerOfTen(int exponent); + void Times10() { return MultiplyByUInt32(10); } + // Pseudocode: + // int result = this / other; + // this = this % other; + // In the worst case this function is in O(this/other). + uint16_t DivideModuloIntBignum(const Bignum& other); + + bool ToHexString(char* buffer, int buffer_size) const; + + static int Compare(const Bignum& a, const Bignum& b); + static bool Equal(const Bignum& a, const Bignum& b) { + return Compare(a, b) == 0; + } + static bool LessEqual(const Bignum& a, const Bignum& b) { + return Compare(a, b) <= 0; + } + static bool Less(const Bignum& a, const Bignum& b) { + return Compare(a, b) < 0; + } + // Returns Compare(a + b, c); + static int PlusCompare(const Bignum& a, const Bignum& b, const Bignum& c); + // Returns a + b == c + static bool PlusEqual(const Bignum& a, const Bignum& b, const Bignum& c) { + return PlusCompare(a, b, c) == 0; + } + // Returns a + b <= c + static bool PlusLessEqual(const Bignum& a, const Bignum& b, const Bignum& c) { + return PlusCompare(a, b, c) <= 0; + } + // Returns a + b < c + static bool PlusLess(const Bignum& a, const Bignum& b, const Bignum& c) { + return PlusCompare(a, b, c) < 0; + } + private: + typedef uint32_t Chunk; + typedef uint64_t DoubleChunk; + + static const int kChunkSize = sizeof(Chunk) * 8; + static const int kDoubleChunkSize = sizeof(DoubleChunk) * 8; + // With bigit size of 28 we loose some bits, but a double still fits easily + // into two chunks, and more importantly we can use the Comba multiplication. + static const int kBigitSize = 28; + static const Chunk kBigitMask = (1 << kBigitSize) - 1; + // Every instance allocates kBigitLength chunks on the stack. Bignums cannot + // grow. There are no checks if the stack-allocated space is sufficient. + static const int kBigitCapacity = kMaxSignificantBits / kBigitSize; + + void EnsureCapacity(int size) { + if (size > kBigitCapacity) { + UNREACHABLE(); + } + } + void Align(const Bignum& other); + void Clamp(); + bool IsClamped() const; + void Zero(); + // Requires this to have enough capacity (no tests done). + // Updates used_digits_ if necessary. + // shift_amount must be < kBigitSize. + void BigitsShiftLeft(int shift_amount); + // BigitLength includes the "hidden" digits encoded in the exponent. + int BigitLength() const { return used_digits_ + exponent_; } + Chunk BigitAt(int index) const; + void SubtractTimes(const Bignum& other, int factor); + + Chunk bigits_buffer_[kBigitCapacity]; + // A vector backed by bigits_buffer_. This way accesses to the array are + // checked for out-of-bounds errors. + Vector<Chunk> bigits_; + int used_digits_; + // The Bignum's value equals value(bigits_) * 2^(exponent_ * kBigitSize). + int exponent_; + + DISALLOW_COPY_AND_ASSIGN(Bignum); + }; + +} // namespace double_conversion + +} // namespace WTF + +#endif // DOUBLE_CONVERSION_BIGNUM_H_ diff --git a/Source/JavaScriptCore/wtf/dtoa/cached-powers.cc b/Source/JavaScriptCore/wtf/dtoa/cached-powers.cc new file mode 100644 index 000000000..54cb7cadd --- /dev/null +++ b/Source/JavaScriptCore/wtf/dtoa/cached-powers.cc @@ -0,0 +1,193 @@ +// Copyright 2006-2008 the V8 project authors. 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 Google Inc. 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 "config.h" + +#include <stdarg.h> +#include <limits.h> +#include <math.h> + +#include "UnusedParam.h" +#include "utils.h" +#include "cached-powers.h" + +namespace WTF { + +namespace double_conversion { + + struct CachedPower { + uint64_t significand; + int16_t binary_exponent; + int16_t decimal_exponent; + }; + + static int kCachedPowersLength = 1; + static int kCachedPowersOffset = 1; + static const double kD_1_LOG2_10 = 0.30102999566398114; // 1 / lg(10) + static CachedPower* kCachedPowers = 0; + + int PowersOfTenCache::kDecimalExponentDistance = 1; + int PowersOfTenCache::kMinDecimalExponent = 1; + int PowersOfTenCache::kMaxDecimalExponent = 1; + + void initialize() { + if (kCachedPowers) + return; + static CachedPower cachedPowers[] = { + {UINT64_2PART_C(0xfa8fd5a0, 081c0288), -1220, -348}, + {UINT64_2PART_C(0xbaaee17f, a23ebf76), -1193, -340}, + {UINT64_2PART_C(0x8b16fb20, 3055ac76), -1166, -332}, + {UINT64_2PART_C(0xcf42894a, 5dce35ea), -1140, -324}, + {UINT64_2PART_C(0x9a6bb0aa, 55653b2d), -1113, -316}, + {UINT64_2PART_C(0xe61acf03, 3d1a45df), -1087, -308}, + {UINT64_2PART_C(0xab70fe17, c79ac6ca), -1060, -300}, + {UINT64_2PART_C(0xff77b1fc, bebcdc4f), -1034, -292}, + {UINT64_2PART_C(0xbe5691ef, 416bd60c), -1007, -284}, + {UINT64_2PART_C(0x8dd01fad, 907ffc3c), -980, -276}, + {UINT64_2PART_C(0xd3515c28, 31559a83), -954, -268}, + {UINT64_2PART_C(0x9d71ac8f, ada6c9b5), -927, -260}, + {UINT64_2PART_C(0xea9c2277, 23ee8bcb), -901, -252}, + {UINT64_2PART_C(0xaecc4991, 4078536d), -874, -244}, + {UINT64_2PART_C(0x823c1279, 5db6ce57), -847, -236}, + {UINT64_2PART_C(0xc2109436, 4dfb5637), -821, -228}, + {UINT64_2PART_C(0x9096ea6f, 3848984f), -794, -220}, + {UINT64_2PART_C(0xd77485cb, 25823ac7), -768, -212}, + {UINT64_2PART_C(0xa086cfcd, 97bf97f4), -741, -204}, + {UINT64_2PART_C(0xef340a98, 172aace5), -715, -196}, + {UINT64_2PART_C(0xb23867fb, 2a35b28e), -688, -188}, + {UINT64_2PART_C(0x84c8d4df, d2c63f3b), -661, -180}, + {UINT64_2PART_C(0xc5dd4427, 1ad3cdba), -635, -172}, + {UINT64_2PART_C(0x936b9fce, bb25c996), -608, -164}, + {UINT64_2PART_C(0xdbac6c24, 7d62a584), -582, -156}, + {UINT64_2PART_C(0xa3ab6658, 0d5fdaf6), -555, -148}, + {UINT64_2PART_C(0xf3e2f893, dec3f126), -529, -140}, + {UINT64_2PART_C(0xb5b5ada8, aaff80b8), -502, -132}, + {UINT64_2PART_C(0x87625f05, 6c7c4a8b), -475, -124}, + {UINT64_2PART_C(0xc9bcff60, 34c13053), -449, -116}, + {UINT64_2PART_C(0x964e858c, 91ba2655), -422, -108}, + {UINT64_2PART_C(0xdff97724, 70297ebd), -396, -100}, + {UINT64_2PART_C(0xa6dfbd9f, b8e5b88f), -369, -92}, + {UINT64_2PART_C(0xf8a95fcf, 88747d94), -343, -84}, + {UINT64_2PART_C(0xb9447093, 8fa89bcf), -316, -76}, + {UINT64_2PART_C(0x8a08f0f8, bf0f156b), -289, -68}, + {UINT64_2PART_C(0xcdb02555, 653131b6), -263, -60}, + {UINT64_2PART_C(0x993fe2c6, d07b7fac), -236, -52}, + {UINT64_2PART_C(0xe45c10c4, 2a2b3b06), -210, -44}, + {UINT64_2PART_C(0xaa242499, 697392d3), -183, -36}, + {UINT64_2PART_C(0xfd87b5f2, 8300ca0e), -157, -28}, + {UINT64_2PART_C(0xbce50864, 92111aeb), -130, -20}, + {UINT64_2PART_C(0x8cbccc09, 6f5088cc), -103, -12}, + {UINT64_2PART_C(0xd1b71758, e219652c), -77, -4}, + {UINT64_2PART_C(0x9c400000, 00000000), -50, 4}, + {UINT64_2PART_C(0xe8d4a510, 00000000), -24, 12}, + {UINT64_2PART_C(0xad78ebc5, ac620000), 3, 20}, + {UINT64_2PART_C(0x813f3978, f8940984), 30, 28}, + {UINT64_2PART_C(0xc097ce7b, c90715b3), 56, 36}, + {UINT64_2PART_C(0x8f7e32ce, 7bea5c70), 83, 44}, + {UINT64_2PART_C(0xd5d238a4, abe98068), 109, 52}, + {UINT64_2PART_C(0x9f4f2726, 179a2245), 136, 60}, + {UINT64_2PART_C(0xed63a231, d4c4fb27), 162, 68}, + {UINT64_2PART_C(0xb0de6538, 8cc8ada8), 189, 76}, + {UINT64_2PART_C(0x83c7088e, 1aab65db), 216, 84}, + {UINT64_2PART_C(0xc45d1df9, 42711d9a), 242, 92}, + {UINT64_2PART_C(0x924d692c, a61be758), 269, 100}, + {UINT64_2PART_C(0xda01ee64, 1a708dea), 295, 108}, + {UINT64_2PART_C(0xa26da399, 9aef774a), 322, 116}, + {UINT64_2PART_C(0xf209787b, b47d6b85), 348, 124}, + {UINT64_2PART_C(0xb454e4a1, 79dd1877), 375, 132}, + {UINT64_2PART_C(0x865b8692, 5b9bc5c2), 402, 140}, + {UINT64_2PART_C(0xc83553c5, c8965d3d), 428, 148}, + {UINT64_2PART_C(0x952ab45c, fa97a0b3), 455, 156}, + {UINT64_2PART_C(0xde469fbd, 99a05fe3), 481, 164}, + {UINT64_2PART_C(0xa59bc234, db398c25), 508, 172}, + {UINT64_2PART_C(0xf6c69a72, a3989f5c), 534, 180}, + {UINT64_2PART_C(0xb7dcbf53, 54e9bece), 561, 188}, + {UINT64_2PART_C(0x88fcf317, f22241e2), 588, 196}, + {UINT64_2PART_C(0xcc20ce9b, d35c78a5), 614, 204}, + {UINT64_2PART_C(0x98165af3, 7b2153df), 641, 212}, + {UINT64_2PART_C(0xe2a0b5dc, 971f303a), 667, 220}, + {UINT64_2PART_C(0xa8d9d153, 5ce3b396), 694, 228}, + {UINT64_2PART_C(0xfb9b7cd9, a4a7443c), 720, 236}, + {UINT64_2PART_C(0xbb764c4c, a7a44410), 747, 244}, + {UINT64_2PART_C(0x8bab8eef, b6409c1a), 774, 252}, + {UINT64_2PART_C(0xd01fef10, a657842c), 800, 260}, + {UINT64_2PART_C(0x9b10a4e5, e9913129), 827, 268}, + {UINT64_2PART_C(0xe7109bfb, a19c0c9d), 853, 276}, + {UINT64_2PART_C(0xac2820d9, 623bf429), 880, 284}, + {UINT64_2PART_C(0x80444b5e, 7aa7cf85), 907, 292}, + {UINT64_2PART_C(0xbf21e440, 03acdd2d), 933, 300}, + {UINT64_2PART_C(0x8e679c2f, 5e44ff8f), 960, 308}, + {UINT64_2PART_C(0xd433179d, 9c8cb841), 986, 316}, + {UINT64_2PART_C(0x9e19db92, b4e31ba9), 1013, 324}, + {UINT64_2PART_C(0xeb96bf6e, badf77d9), 1039, 332}, + {UINT64_2PART_C(0xaf87023b, 9bf0ee6b), 1066, 340}, + }; + kCachedPowers = cachedPowers; + kCachedPowersLength = ARRAY_SIZE(cachedPowers); + kCachedPowersOffset = -cachedPowers[0].decimal_exponent; + PowersOfTenCache::kDecimalExponentDistance = kCachedPowers[1].decimal_exponent - kCachedPowers[0].decimal_exponent; + PowersOfTenCache::kMinDecimalExponent = kCachedPowers[0].decimal_exponent; + PowersOfTenCache::kMaxDecimalExponent = kCachedPowers[kCachedPowersLength - 1].decimal_exponent; + } + + void PowersOfTenCache::GetCachedPowerForBinaryExponentRange( + int min_exponent, + int max_exponent, + DiyFp* power, + int* decimal_exponent) { + UNUSED_PARAM(max_exponent); + int kQ = DiyFp::kSignificandSize; + double k = ceil((min_exponent + kQ - 1) * kD_1_LOG2_10); + int foo = kCachedPowersOffset; + int index = + (foo + static_cast<int>(k) - 1) / kDecimalExponentDistance + 1; + ASSERT(0 <= index && index < kCachedPowersLength); + CachedPower cached_power = kCachedPowers[index]; + ASSERT(min_exponent <= cached_power.binary_exponent); + ASSERT(cached_power.binary_exponent <= max_exponent); + *decimal_exponent = cached_power.decimal_exponent; + *power = DiyFp(cached_power.significand, cached_power.binary_exponent); + } + + + void PowersOfTenCache::GetCachedPowerForDecimalExponent(int requested_exponent, + DiyFp* power, + int* found_exponent) { + ASSERT(kMinDecimalExponent <= requested_exponent); + ASSERT(requested_exponent < kMaxDecimalExponent + kDecimalExponentDistance); + int index = + (requested_exponent + kCachedPowersOffset) / kDecimalExponentDistance; + CachedPower cached_power = kCachedPowers[index]; + *power = DiyFp(cached_power.significand, cached_power.binary_exponent); + *found_exponent = cached_power.decimal_exponent; + ASSERT(*found_exponent <= requested_exponent); + ASSERT(requested_exponent < *found_exponent + kDecimalExponentDistance); + } + +} // namespace double_conversion + +} // namespace WTF diff --git a/Source/JavaScriptCore/wtf/dtoa/cached-powers.h b/Source/JavaScriptCore/wtf/dtoa/cached-powers.h new file mode 100644 index 000000000..cbc04d43d --- /dev/null +++ b/Source/JavaScriptCore/wtf/dtoa/cached-powers.h @@ -0,0 +1,72 @@ +// Copyright 2010 the V8 project authors. 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 Google Inc. 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. + +#ifndef DOUBLE_CONVERSION_CACHED_POWERS_H_ +#define DOUBLE_CONVERSION_CACHED_POWERS_H_ + +#include "diy-fp.h" + +namespace WTF { + +namespace double_conversion { + + class PowersOfTenCache { + public: + + // Not all powers of ten are cached. The decimal exponent of two neighboring + // cached numbers will differ by kDecimalExponentDistance. + static int kDecimalExponentDistance; + + static int kMinDecimalExponent; + static int kMaxDecimalExponent; + + // Returns a cached power-of-ten with a binary exponent in the range + // [min_exponent; max_exponent] (boundaries included). + static void GetCachedPowerForBinaryExponentRange(int min_exponent, + int max_exponent, + DiyFp* power, + int* decimal_exponent); + + // Returns a cached power of ten x ~= 10^k such that + // k <= decimal_exponent < k + kCachedPowersDecimalDistance. + // The given decimal_exponent must satisfy + // kMinDecimalExponent <= requested_exponent, and + // requested_exponent < kMaxDecimalExponent + kDecimalExponentDistance. + static void GetCachedPowerForDecimalExponent(int requested_exponent, + DiyFp* power, + int* found_exponent); + }; + + // Initializes the table of cached powers used by the dtoa algorithm. + // This needs to be called when JSC is being initialized. + void initialize(); + +} // namespace double_conversion + +} // namespace WTF + +#endif // DOUBLE_CONVERSION_CACHED_POWERS_H_ diff --git a/Source/JavaScriptCore/wtf/dtoa/diy-fp.cc b/Source/JavaScriptCore/wtf/dtoa/diy-fp.cc new file mode 100644 index 000000000..c0233595f --- /dev/null +++ b/Source/JavaScriptCore/wtf/dtoa/diy-fp.cc @@ -0,0 +1,62 @@ +// Copyright 2010 the V8 project authors. 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 Google Inc. 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 "config.h" + +#include "diy-fp.h" +#include "utils.h" + +namespace WTF { + +namespace double_conversion { + + void DiyFp::Multiply(const DiyFp& other) { + // Simply "emulates" a 128 bit multiplication. + // However: the resulting number only contains 64 bits. The least + // significant 64 bits are only used for rounding the most significant 64 + // bits. + const uint64_t kM32 = 0xFFFFFFFFU; + uint64_t a = f_ >> 32; + uint64_t b = f_ & kM32; + uint64_t c = other.f_ >> 32; + uint64_t d = other.f_ & kM32; + uint64_t ac = a * c; + uint64_t bc = b * c; + uint64_t ad = a * d; + uint64_t bd = b * d; + uint64_t tmp = (bd >> 32) + (ad & kM32) + (bc & kM32); + // By adding 1U << 31 to tmp we round the final result. + // Halfway cases will be round up. + tmp += 1U << 31; + uint64_t result_f = ac + (ad >> 32) + (bc >> 32) + (tmp >> 32); + e_ += other.e_ + 64; + f_ = result_f; + } + +} // namespace double_conversion + +} // namespace WTF diff --git a/Source/JavaScriptCore/wtf/dtoa/diy-fp.h b/Source/JavaScriptCore/wtf/dtoa/diy-fp.h new file mode 100644 index 000000000..e843100a8 --- /dev/null +++ b/Source/JavaScriptCore/wtf/dtoa/diy-fp.h @@ -0,0 +1,122 @@ +// Copyright 2010 the V8 project authors. 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 Google Inc. 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. + +#ifndef DOUBLE_CONVERSION_DIY_FP_H_ +#define DOUBLE_CONVERSION_DIY_FP_H_ + +#include "utils.h" + +namespace WTF { + +namespace double_conversion { + + // This "Do It Yourself Floating Point" class implements a floating-point number + // with a uint64 significand and an int exponent. Normalized DiyFp numbers will + // have the most significant bit of the significand set. + // Multiplication and Subtraction do not normalize their results. + // DiyFp are not designed to contain special doubles (NaN and Infinity). + class DiyFp { + public: + static const int kSignificandSize = 64; + + DiyFp() : f_(0), e_(0) {} + DiyFp(uint64_t f, int e) : f_(f), e_(e) {} + + // this = this - other. + // The exponents of both numbers must be the same and the significand of this + // must be bigger than the significand of other. + // The result will not be normalized. + void Subtract(const DiyFp& other) { + ASSERT(e_ == other.e_); + ASSERT(f_ >= other.f_); + f_ -= other.f_; + } + + // Returns a - b. + // The exponents of both numbers must be the same and this must be bigger + // than other. The result will not be normalized. + static DiyFp Minus(const DiyFp& a, const DiyFp& b) { + DiyFp result = a; + result.Subtract(b); + return result; + } + + + // this = this * other. + void Multiply(const DiyFp& other); + + // returns a * b; + static DiyFp Times(const DiyFp& a, const DiyFp& b) { + DiyFp result = a; + result.Multiply(b); + return result; + } + + void Normalize() { + ASSERT(f_ != 0); + uint64_t f = f_; + int e = e_; + + // This method is mainly called for normalizing boundaries. In general + // boundaries need to be shifted by 10 bits. We thus optimize for this case. + const uint64_t k10MSBits = UINT64_2PART_C(0xFFC00000, 00000000); + while ((f & k10MSBits) == 0) { + f <<= 10; + e -= 10; + } + while ((f & kUint64MSB) == 0) { + f <<= 1; + e--; + } + f_ = f; + e_ = e; + } + + static DiyFp Normalize(const DiyFp& a) { + DiyFp result = a; + result.Normalize(); + return result; + } + + uint64_t f() const { return f_; } + int e() const { return e_; } + + void set_f(uint64_t new_value) { f_ = new_value; } + void set_e(int new_value) { e_ = new_value; } + + private: + static const uint64_t kUint64MSB = UINT64_2PART_C(0x80000000, 00000000); + + uint64_t f_; + int e_; + }; + +} // namespace double_conversion + +} // namespace WTF + +#endif // DOUBLE_CONVERSION_DIY_FP_H_ diff --git a/Source/JavaScriptCore/wtf/dtoa/double-conversion.cc b/Source/JavaScriptCore/wtf/dtoa/double-conversion.cc new file mode 100644 index 000000000..cab1a51f2 --- /dev/null +++ b/Source/JavaScriptCore/wtf/dtoa/double-conversion.cc @@ -0,0 +1,870 @@ +// Copyright 2010 the V8 project authors. 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 Google Inc. 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 "config.h" + +#include <limits.h> +#include <math.h> + +#include "double-conversion.h" + +#include "bignum-dtoa.h" +#include "double.h" +#include "fast-dtoa.h" +#include "fixed-dtoa.h" +#include "strtod.h" +#include "utils.h" + +namespace WTF { + +namespace double_conversion { + + const DoubleToStringConverter& DoubleToStringConverter::EcmaScriptConverter() { + int flags = UNIQUE_ZERO | EMIT_POSITIVE_EXPONENT_SIGN; + static DoubleToStringConverter converter(flags, + "Infinity", + "NaN", + 'e', + -6, 21, + 6, 0); + return converter; + } + + + bool DoubleToStringConverter::HandleSpecialValues( + double value, + StringBuilder* result_builder) const { + Double double_inspect(value); + if (double_inspect.IsInfinite()) { + if (infinity_symbol_ == NULL) return false; + if (value < 0) { + result_builder->AddCharacter('-'); + } + result_builder->AddString(infinity_symbol_); + return true; + } + if (double_inspect.IsNan()) { + if (nan_symbol_ == NULL) return false; + result_builder->AddString(nan_symbol_); + return true; + } + return false; + } + + + void DoubleToStringConverter::CreateExponentialRepresentation( + const char* decimal_digits, + int length, + int exponent, + StringBuilder* result_builder) const { + ASSERT(length != 0); + result_builder->AddCharacter(decimal_digits[0]); + if (length != 1) { + result_builder->AddCharacter('.'); + result_builder->AddSubstring(&decimal_digits[1], length-1); + } + result_builder->AddCharacter(exponent_character_); + if (exponent < 0) { + result_builder->AddCharacter('-'); + exponent = -exponent; + } else { + if ((flags_ & EMIT_POSITIVE_EXPONENT_SIGN) != 0) { + result_builder->AddCharacter('+'); + } + } + if (exponent == 0) { + result_builder->AddCharacter('0'); + return; + } + ASSERT(exponent < 1e4); + const int kMaxExponentLength = 5; + char buffer[kMaxExponentLength]; + int first_char_pos = kMaxExponentLength; + while (exponent > 0) { + buffer[--first_char_pos] = '0' + (exponent % 10); + exponent /= 10; + } + result_builder->AddSubstring(&buffer[first_char_pos], + kMaxExponentLength - first_char_pos); + } + + + void DoubleToStringConverter::CreateDecimalRepresentation( + const char* decimal_digits, + int length, + int decimal_point, + int digits_after_point, + StringBuilder* result_builder) const { + // Create a representation that is padded with zeros if needed. + if (decimal_point <= 0) { + // "0.00000decimal_rep". + result_builder->AddCharacter('0'); + if (digits_after_point > 0) { + result_builder->AddCharacter('.'); + result_builder->AddPadding('0', -decimal_point); + ASSERT(length <= digits_after_point - (-decimal_point)); + result_builder->AddSubstring(decimal_digits, length); + int remaining_digits = digits_after_point - (-decimal_point) - length; + result_builder->AddPadding('0', remaining_digits); + } + } else if (decimal_point >= length) { + // "decimal_rep0000.00000" or "decimal_rep.0000" + result_builder->AddSubstring(decimal_digits, length); + result_builder->AddPadding('0', decimal_point - length); + if (digits_after_point > 0) { + result_builder->AddCharacter('.'); + result_builder->AddPadding('0', digits_after_point); + } + } else { + // "decima.l_rep000" + ASSERT(digits_after_point > 0); + result_builder->AddSubstring(decimal_digits, decimal_point); + result_builder->AddCharacter('.'); + ASSERT(length - decimal_point <= digits_after_point); + result_builder->AddSubstring(&decimal_digits[decimal_point], + length - decimal_point); + int remaining_digits = digits_after_point - (length - decimal_point); + result_builder->AddPadding('0', remaining_digits); + } + if (digits_after_point == 0) { + if ((flags_ & EMIT_TRAILING_DECIMAL_POINT) != 0) { + result_builder->AddCharacter('.'); + } + if ((flags_ & EMIT_TRAILING_ZERO_AFTER_POINT) != 0) { + result_builder->AddCharacter('0'); + } + } + } + + + bool DoubleToStringConverter::ToShortest(double value, + StringBuilder* result_builder) const { + if (Double(value).IsSpecial()) { + return HandleSpecialValues(value, result_builder); + } + + int decimal_point; + bool sign; + const int kDecimalRepCapacity = kBase10MaximalLength + 1; + char decimal_rep[kDecimalRepCapacity]; + int decimal_rep_length; + + DoubleToAscii(value, SHORTEST, 0, decimal_rep, kDecimalRepCapacity, + &sign, &decimal_rep_length, &decimal_point); + + bool unique_zero = (flags_ & UNIQUE_ZERO) != 0; + if (sign && (value != 0.0 || !unique_zero)) { + result_builder->AddCharacter('-'); + } + + int exponent = decimal_point - 1; + if ((decimal_in_shortest_low_ <= exponent) && + (exponent < decimal_in_shortest_high_)) { + CreateDecimalRepresentation(decimal_rep, decimal_rep_length, + decimal_point, + Max(0, decimal_rep_length - decimal_point), + result_builder); + } else { + CreateExponentialRepresentation(decimal_rep, decimal_rep_length, exponent, + result_builder); + } + return true; + } + + + bool DoubleToStringConverter::ToFixed(double value, + int requested_digits, + StringBuilder* result_builder) const { + ASSERT(kMaxFixedDigitsBeforePoint == 60); + const double kFirstNonFixed = 1e60; + + if (Double(value).IsSpecial()) { + return HandleSpecialValues(value, result_builder); + } + + if (requested_digits > kMaxFixedDigitsAfterPoint) return false; + if (value >= kFirstNonFixed || value <= -kFirstNonFixed) return false; + + // Find a sufficiently precise decimal representation of n. + int decimal_point; + bool sign; + // Add space for the '\0' byte. + const int kDecimalRepCapacity = + kMaxFixedDigitsBeforePoint + kMaxFixedDigitsAfterPoint + 1; + char decimal_rep[kDecimalRepCapacity]; + int decimal_rep_length; + DoubleToAscii(value, FIXED, requested_digits, + decimal_rep, kDecimalRepCapacity, + &sign, &decimal_rep_length, &decimal_point); + + bool unique_zero = ((flags_ & UNIQUE_ZERO) != 0); + if (sign && (value != 0.0 || !unique_zero)) { + result_builder->AddCharacter('-'); + } + + CreateDecimalRepresentation(decimal_rep, decimal_rep_length, decimal_point, + requested_digits, result_builder); + return true; + } + + + bool DoubleToStringConverter::ToExponential( + double value, + int requested_digits, + StringBuilder* result_builder) const { + if (Double(value).IsSpecial()) { + return HandleSpecialValues(value, result_builder); + } + + if (requested_digits < -1) return false; + if (requested_digits > kMaxExponentialDigits) return false; + + int decimal_point; + bool sign; + // Add space for digit before the decimal point and the '\0' character. + const int kDecimalRepCapacity = kMaxExponentialDigits + 2; + ASSERT(kDecimalRepCapacity > kBase10MaximalLength); + char decimal_rep[kDecimalRepCapacity]; + int decimal_rep_length; + + if (requested_digits == -1) { + DoubleToAscii(value, SHORTEST, 0, + decimal_rep, kDecimalRepCapacity, + &sign, &decimal_rep_length, &decimal_point); + } else { + DoubleToAscii(value, PRECISION, requested_digits + 1, + decimal_rep, kDecimalRepCapacity, + &sign, &decimal_rep_length, &decimal_point); + ASSERT(decimal_rep_length <= requested_digits + 1); + + for (int i = decimal_rep_length; i < requested_digits + 1; ++i) { + decimal_rep[i] = '0'; + } + decimal_rep_length = requested_digits + 1; + } + + bool unique_zero = ((flags_ & UNIQUE_ZERO) != 0); + if (sign && (value != 0.0 || !unique_zero)) { + result_builder->AddCharacter('-'); + } + + int exponent = decimal_point - 1; + CreateExponentialRepresentation(decimal_rep, + decimal_rep_length, + exponent, + result_builder); + return true; + } + + + bool DoubleToStringConverter::ToPrecision(double value, + int precision, + StringBuilder* result_builder) const { + if (Double(value).IsSpecial()) { + return HandleSpecialValues(value, result_builder); + } + + if (precision < kMinPrecisionDigits || precision > kMaxPrecisionDigits) { + return false; + } + + // Find a sufficiently precise decimal representation of n. + int decimal_point; + bool sign; + // Add one for the terminating null character. + const int kDecimalRepCapacity = kMaxPrecisionDigits + 1; + char decimal_rep[kDecimalRepCapacity]; + int decimal_rep_length; + + DoubleToAscii(value, PRECISION, precision, + decimal_rep, kDecimalRepCapacity, + &sign, &decimal_rep_length, &decimal_point); + ASSERT(decimal_rep_length <= precision); + + bool unique_zero = ((flags_ & UNIQUE_ZERO) != 0); + if (sign && (value != 0.0 || !unique_zero)) { + result_builder->AddCharacter('-'); + } + + // The exponent if we print the number as x.xxeyyy. That is with the + // decimal point after the first digit. + int exponent = decimal_point - 1; + + int extra_zero = ((flags_ & EMIT_TRAILING_ZERO_AFTER_POINT) != 0) ? 1 : 0; + if ((-decimal_point + 1 > max_leading_padding_zeroes_in_precision_mode_) || + (decimal_point - precision + extra_zero > + max_trailing_padding_zeroes_in_precision_mode_)) { + // Fill buffer to contain 'precision' digits. + // Usually the buffer is already at the correct length, but 'DoubleToAscii' + // is allowed to return less characters. + for (int i = decimal_rep_length; i < precision; ++i) { + decimal_rep[i] = '0'; + } + + CreateExponentialRepresentation(decimal_rep, + precision, + exponent, + result_builder); + } else { + CreateDecimalRepresentation(decimal_rep, decimal_rep_length, decimal_point, + Max(0, precision - decimal_point), + result_builder); + } + return true; + } + + + static BignumDtoaMode DtoaToBignumDtoaMode( + DoubleToStringConverter::DtoaMode dtoa_mode) { + switch (dtoa_mode) { + case DoubleToStringConverter::SHORTEST: return BIGNUM_DTOA_SHORTEST; + case DoubleToStringConverter::FIXED: return BIGNUM_DTOA_FIXED; + case DoubleToStringConverter::PRECISION: return BIGNUM_DTOA_PRECISION; + default: + UNREACHABLE(); + return BIGNUM_DTOA_SHORTEST; // To silence compiler. + } + } + + + void DoubleToStringConverter::DoubleToAscii(double v, + DtoaMode mode, + int requested_digits, + char* buffer, + int buffer_length, + bool* sign, + int* length, + int* point) { + Vector<char> vector(buffer, buffer_length); + ASSERT(!Double(v).IsSpecial()); + ASSERT(mode == SHORTEST || requested_digits >= 0); + + if (Double(v).Sign() < 0) { + *sign = true; + v = -v; + } else { + *sign = false; + } + + if (mode == PRECISION && requested_digits == 0) { + vector[0] = '\0'; + *length = 0; + return; + } + + if (v == 0) { + vector[0] = '0'; + vector[1] = '\0'; + *length = 1; + *point = 1; + return; + } + + bool fast_worked; + switch (mode) { + case SHORTEST: + fast_worked = FastDtoa(v, FAST_DTOA_SHORTEST, 0, vector, length, point); + break; + case FIXED: + fast_worked = FastFixedDtoa(v, requested_digits, vector, length, point); + break; + case PRECISION: + fast_worked = FastDtoa(v, FAST_DTOA_PRECISION, requested_digits, + vector, length, point); + break; + default: + UNREACHABLE(); + fast_worked = false; + } + if (fast_worked) return; + + // If the fast dtoa didn't succeed use the slower bignum version. + BignumDtoaMode bignum_mode = DtoaToBignumDtoaMode(mode); + BignumDtoa(v, bignum_mode, requested_digits, vector, length, point); + vector[*length] = '\0'; + } + + + // Consumes the given substring from the iterator. + // Returns false, if the substring does not match. + static bool ConsumeSubString(const char** current, + const char* end, + const char* substring) { + ASSERT(**current == *substring); + for (substring++; *substring != '\0'; substring++) { + ++*current; + if (*current == end || **current != *substring) return false; + } + ++*current; + return true; + } + + + // Maximum number of significant digits in decimal representation. + // The longest possible double in decimal representation is + // (2^53 - 1) * 2 ^ -1074 that is (2 ^ 53 - 1) * 5 ^ 1074 / 10 ^ 1074 + // (768 digits). If we parse a number whose first digits are equal to a + // mean of 2 adjacent doubles (that could have up to 769 digits) the result + // must be rounded to the bigger one unless the tail consists of zeros, so + // we don't need to preserve all the digits. + const int kMaxSignificantDigits = 772; + + + // Returns true if a nonspace found and false if the end has reached. + static inline bool AdvanceToNonspace(const char** current, const char* end) { + while (*current != end) { + if (**current != ' ') return true; + ++*current; + } + return false; + } + + + static bool isDigit(int x, int radix) { + return (x >= '0' && x <= '9' && x < '0' + radix) + || (radix > 10 && x >= 'a' && x < 'a' + radix - 10) + || (radix > 10 && x >= 'A' && x < 'A' + radix - 10); + } + + + static double SignedZero(bool sign) { + return sign ? -0.0 : 0.0; + } + + + // Parsing integers with radix 2, 4, 8, 16, 32. Assumes current != end. + template <int radix_log_2> + static double RadixStringToDouble(const char* current, + const char* end, + bool sign, + bool allow_trailing_junk, + double junk_string_value, + const char** trailing_pointer) { + ASSERT(current != end); + + // Skip leading 0s. + while (*current == '0') { + ++current; + if (current == end) { + *trailing_pointer = end; + return SignedZero(sign); + } + } + + int64_t number = 0; + int exponent = 0; + const int radix = (1 << radix_log_2); + + do { + int digit; + if (*current >= '0' && *current <= '9' && *current < '0' + radix) { + digit = static_cast<char>(*current) - '0'; + } else if (radix > 10 && *current >= 'a' && *current < 'a' + radix - 10) { + digit = static_cast<char>(*current) - 'a' + 10; + } else if (radix > 10 && *current >= 'A' && *current < 'A' + radix - 10) { + digit = static_cast<char>(*current) - 'A' + 10; + } else { + if (allow_trailing_junk || !AdvanceToNonspace(¤t, end)) { + break; + } else { + return junk_string_value; + } + } + + number = number * radix + digit; + int overflow = static_cast<int>(number >> 53); + if (overflow != 0) { + // Overflow occurred. Need to determine which direction to round the + // result. + int overflow_bits_count = 1; + while (overflow > 1) { + overflow_bits_count++; + overflow >>= 1; + } + + int dropped_bits_mask = ((1 << overflow_bits_count) - 1); + int dropped_bits = static_cast<int>(number) & dropped_bits_mask; + number >>= overflow_bits_count; + exponent = overflow_bits_count; + + bool zero_tail = true; + while (true) { + ++current; + if (current == end || !isDigit(*current, radix)) break; + zero_tail = zero_tail && *current == '0'; + exponent += radix_log_2; + } + + if (!allow_trailing_junk && AdvanceToNonspace(¤t, end)) { + return junk_string_value; + } + + int middle_value = (1 << (overflow_bits_count - 1)); + if (dropped_bits > middle_value) { + number++; // Rounding up. + } else if (dropped_bits == middle_value) { + // Rounding to even to consistency with decimals: half-way case rounds + // up if significant part is odd and down otherwise. + if ((number & 1) != 0 || !zero_tail) { + number++; // Rounding up. + } + } + + // Rounding up may cause overflow. + if ((number & ((int64_t)1 << 53)) != 0) { + exponent++; + number >>= 1; + } + break; + } + ++current; + } while (current != end); + + ASSERT(number < ((int64_t)1 << 53)); + ASSERT(static_cast<int64_t>(static_cast<double>(number)) == number); + + *trailing_pointer = current; + + if (exponent == 0) { + if (sign) { + if (number == 0) return -0.0; + number = -number; + } + return static_cast<double>(number); + } + + ASSERT(number != 0); + return Double(DiyFp(number, exponent)).value(); + } + + + double StringToDoubleConverter::StringToDouble( + const char* input, + int length, + int* processed_characters_count) { + const char* current = input; + const char* end = input + length; + + *processed_characters_count = 0; + + const bool allow_trailing_junk = (flags_ & ALLOW_TRAILING_JUNK) != 0; + const bool allow_leading_spaces = (flags_ & ALLOW_LEADING_SPACES) != 0; + const bool allow_trailing_spaces = (flags_ & ALLOW_TRAILING_SPACES) != 0; + const bool allow_spaces_after_sign = (flags_ & ALLOW_SPACES_AFTER_SIGN) != 0; + + // To make sure that iterator dereferencing is valid the following + // convention is used: + // 1. Each '++current' statement is followed by check for equality to 'end'. + // 2. If AdvanceToNonspace returned false then current == end. + // 3. If 'current' becomes equal to 'end' the function returns or goes to + // 'parsing_done'. + // 4. 'current' is not dereferenced after the 'parsing_done' label. + // 5. Code before 'parsing_done' may rely on 'current != end'. + if (current == end) return empty_string_value_; + + if (allow_leading_spaces || allow_trailing_spaces) { + if (!AdvanceToNonspace(¤t, end)) { + *processed_characters_count = current - input; + return empty_string_value_; + } + if (!allow_leading_spaces && (input != current)) { + // No leading spaces allowed, but AdvanceToNonspace moved forward. + return junk_string_value_; + } + } + + // The longest form of simplified number is: "-<significant digits>.1eXXX\0". + const int kBufferSize = kMaxSignificantDigits + 10; + char buffer[kBufferSize]; // NOLINT: size is known at compile time. + int buffer_pos = 0; + + // Exponent will be adjusted if insignificant digits of the integer part + // or insignificant leading zeros of the fractional part are dropped. + int exponent = 0; + int significant_digits = 0; + int insignificant_digits = 0; + bool nonzero_digit_dropped = false; + bool sign = false; + + if (*current == '+' || *current == '-') { + sign = (*current == '-'); + ++current; + const char* next_non_space = current; + // Skip following spaces (if allowed). + if (!AdvanceToNonspace(&next_non_space, end)) return junk_string_value_; + if (!allow_spaces_after_sign && (current != next_non_space)) { + return junk_string_value_; + } + current = next_non_space; + } + + if (infinity_symbol_ != NULL) { + if (*current == infinity_symbol_[0]) { + if (!ConsumeSubString(¤t, end, infinity_symbol_)) { + return junk_string_value_; + } + + if (!(allow_trailing_spaces || allow_trailing_junk) && (current != end)) { + return junk_string_value_; + } + if (!allow_trailing_junk && AdvanceToNonspace(¤t, end)) { + return junk_string_value_; + } + + ASSERT(buffer_pos == 0); + *processed_characters_count = current - input; + return sign ? -Double::Infinity() : Double::Infinity(); + } + } + + if (nan_symbol_ != NULL) { + if (*current == nan_symbol_[0]) { + if (!ConsumeSubString(¤t, end, nan_symbol_)) { + return junk_string_value_; + } + + if (!(allow_trailing_spaces || allow_trailing_junk) && (current != end)) { + return junk_string_value_; + } + if (!allow_trailing_junk && AdvanceToNonspace(¤t, end)) { + return junk_string_value_; + } + + ASSERT(buffer_pos == 0); + *processed_characters_count = current - input; + return sign ? -Double::NaN() : Double::NaN(); + } + } + + bool leading_zero = false; + if (*current == '0') { + ++current; + if (current == end) { + *processed_characters_count = current - input; + return SignedZero(sign); + } + + leading_zero = true; + + // It could be hexadecimal value. + if ((flags_ & ALLOW_HEX) && (*current == 'x' || *current == 'X')) { + ++current; + if (current == end || !isDigit(*current, 16)) { + return junk_string_value_; // "0x". + } + + const char* tail_pointer = NULL; + double result = RadixStringToDouble<4>(current, + end, + sign, + allow_trailing_junk, + junk_string_value_, + &tail_pointer); + if (tail_pointer != NULL) { + if (allow_trailing_spaces) AdvanceToNonspace(&tail_pointer, end); + *processed_characters_count = tail_pointer - input; + } + return result; + } + + // Ignore leading zeros in the integer part. + while (*current == '0') { + ++current; + if (current == end) { + *processed_characters_count = current - input; + return SignedZero(sign); + } + } + } + + bool octal = leading_zero && (flags_ & ALLOW_OCTALS) != 0; + + // Copy significant digits of the integer part (if any) to the buffer. + while (*current >= '0' && *current <= '9') { + if (significant_digits < kMaxSignificantDigits) { + ASSERT(buffer_pos < kBufferSize); + buffer[buffer_pos++] = static_cast<char>(*current); + significant_digits++; + // Will later check if it's an octal in the buffer. + } else { + insignificant_digits++; // Move the digit into the exponential part. + nonzero_digit_dropped = nonzero_digit_dropped || *current != '0'; + } + octal = octal && *current < '8'; + ++current; + if (current == end) goto parsing_done; + } + + if (significant_digits == 0) { + octal = false; + } + + if (*current == '.') { + if (octal && !allow_trailing_junk) return junk_string_value_; + if (octal) goto parsing_done; + + ++current; + if (current == end) { + if (significant_digits == 0 && !leading_zero) { + return junk_string_value_; + } else { + goto parsing_done; + } + } + + if (significant_digits == 0) { + // octal = false; + // Integer part consists of 0 or is absent. Significant digits start after + // leading zeros (if any). + while (*current == '0') { + ++current; + if (current == end) { + *processed_characters_count = current - input; + return SignedZero(sign); + } + exponent--; // Move this 0 into the exponent. + } + } + + // There is a fractional part. + while (*current >= '0' && *current <= '9') { + if (significant_digits < kMaxSignificantDigits) { + ASSERT(buffer_pos < kBufferSize); + buffer[buffer_pos++] = static_cast<char>(*current); + significant_digits++; + exponent--; + } else { + // Ignore insignificant digits in the fractional part. + nonzero_digit_dropped = nonzero_digit_dropped || *current != '0'; + } + ++current; + if (current == end) goto parsing_done; + } + } + + if (!leading_zero && exponent == 0 && significant_digits == 0) { + // If leading_zeros is true then the string contains zeros. + // If exponent < 0 then string was [+-]\.0*... + // If significant_digits != 0 the string is not equal to 0. + // Otherwise there are no digits in the string. + return junk_string_value_; + } + + // Parse exponential part. + if (*current == 'e' || *current == 'E') { + if (octal && !allow_trailing_junk) return junk_string_value_; + if (octal) goto parsing_done; + ++current; + if (current == end) { + if (allow_trailing_junk) { + goto parsing_done; + } else { + return junk_string_value_; + } + } + char sign = '+'; + if (*current == '+' || *current == '-') { + sign = static_cast<char>(*current); + ++current; + if (current == end) { + if (allow_trailing_junk) { + goto parsing_done; + } else { + return junk_string_value_; + } + } + } + + if (current == end || *current < '0' || *current > '9') { + if (allow_trailing_junk) { + goto parsing_done; + } else { + return junk_string_value_; + } + } + + const int max_exponent = INT_MAX / 2; + ASSERT(-max_exponent / 2 <= exponent && exponent <= max_exponent / 2); + int num = 0; + do { + // Check overflow. + int digit = *current - '0'; + if (num >= max_exponent / 10 + && !(num == max_exponent / 10 && digit <= max_exponent % 10)) { + num = max_exponent; + } else { + num = num * 10 + digit; + } + ++current; + } while (current != end && *current >= '0' && *current <= '9'); + + exponent += (sign == '-' ? -num : num); + } + + if (!(allow_trailing_spaces || allow_trailing_junk) && (current != end)) { + return junk_string_value_; + } + if (!allow_trailing_junk && AdvanceToNonspace(¤t, end)) { + return junk_string_value_; + } + if (allow_trailing_spaces) { + AdvanceToNonspace(¤t, end); + } + + parsing_done: + exponent += insignificant_digits; + + if (octal) { + double result; + const char* tail_pointer = NULL; + result = RadixStringToDouble<3>(buffer, + buffer + buffer_pos, + sign, + allow_trailing_junk, + junk_string_value_, + &tail_pointer); + ASSERT(tail_pointer != NULL); + *processed_characters_count = current - input; + return result; + } + + if (nonzero_digit_dropped) { + buffer[buffer_pos++] = '1'; + exponent--; + } + + ASSERT(buffer_pos < kBufferSize); + buffer[buffer_pos] = '\0'; + + double converted = Strtod(Vector<const char>(buffer, buffer_pos), exponent); + *processed_characters_count = current - input; + return sign? -converted: converted; + } + +} // namespace double_conversion + +} // namespace WTF diff --git a/Source/JavaScriptCore/wtf/dtoa/double-conversion.h b/Source/JavaScriptCore/wtf/dtoa/double-conversion.h new file mode 100644 index 000000000..4d6fc2f57 --- /dev/null +++ b/Source/JavaScriptCore/wtf/dtoa/double-conversion.h @@ -0,0 +1,502 @@ +// Copyright 2010 the V8 project authors. 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 Google Inc. 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. + +#ifndef DOUBLE_CONVERSION_DOUBLE_CONVERSION_H_ +#define DOUBLE_CONVERSION_DOUBLE_CONVERSION_H_ + +#include "utils.h" + +namespace WTF { + +namespace double_conversion { + + class DoubleToStringConverter { + public: + // When calling ToFixed with a double > 10^kMaxFixedDigitsBeforePoint + // or a requested_digits parameter > kMaxFixedDigitsAfterPoint then the + // function returns false. + static const int kMaxFixedDigitsBeforePoint = 60; + static const int kMaxFixedDigitsAfterPoint = 60; + + // When calling ToExponential with a requested_digits + // parameter > kMaxExponentialDigits then the function returns false. + static const int kMaxExponentialDigits = 120; + + // When calling ToPrecision with a requested_digits + // parameter < kMinPrecisionDigits or requested_digits > kMaxPrecisionDigits + // then the function returns false. + static const int kMinPrecisionDigits = 1; + static const int kMaxPrecisionDigits = 120; + + enum Flags { + NO_FLAGS = 0, + EMIT_POSITIVE_EXPONENT_SIGN = 1, + EMIT_TRAILING_DECIMAL_POINT = 2, + EMIT_TRAILING_ZERO_AFTER_POINT = 4, + UNIQUE_ZERO = 8 + }; + + // Flags should be a bit-or combination of the possible Flags-enum. + // - NO_FLAGS: no special flags. + // - EMIT_POSITIVE_EXPONENT_SIGN: when the number is converted into exponent + // form, emits a '+' for positive exponents. Example: 1.2e+2. + // - EMIT_TRAILING_DECIMAL_POINT: when the input number is an integer and is + // converted into decimal format then a trailing decimal point is appended. + // Example: 2345.0 is converted to "2345.". + // - EMIT_TRAILING_ZERO_AFTER_POINT: in addition to a trailing decimal point + // emits a trailing '0'-character. This flag requires the + // EXMIT_TRAILING_DECIMAL_POINT flag. + // Example: 2345.0 is converted to "2345.0". + // - UNIQUE_ZERO: "-0.0" is converted to "0.0". + // + // Infinity symbol and nan_symbol provide the string representation for these + // special values. If the string is NULL and the special value is encountered + // then the conversion functions return false. + // + // The exponent_character is used in exponential representations. It is + // usually 'e' or 'E'. + // + // When converting to the shortest representation the converter will + // represent input numbers in decimal format if they are in the interval + // [10^decimal_in_shortest_low; 10^decimal_in_shortest_high[ + // (lower boundary included, greater boundary excluded). + // Example: with decimal_in_shortest_low = -6 and + // decimal_in_shortest_high = 21: + // ToShortest(0.000001) -> "0.000001" + // ToShortest(0.0000001) -> "1e-7" + // ToShortest(111111111111111111111.0) -> "111111111111111110000" + // ToShortest(100000000000000000000.0) -> "100000000000000000000" + // ToShortest(1111111111111111111111.0) -> "1.1111111111111111e+21" + // + // When converting to precision mode the converter may add + // max_leading_padding_zeroes before returning the number in exponential + // format. + // Example with max_leading_padding_zeroes_in_precision_mode = 6. + // ToPrecision(0.0000012345, 2) -> "0.0000012" + // ToPrecision(0.00000012345, 2) -> "1.2e-7" + // Similarily the converter may add up to + // max_trailing_padding_zeroes_in_precision_mode in precision mode to avoid + // returning an exponential representation. A zero added by the + // EMIT_TRAILING_ZERO_AFTER_POINT flag is counted for this limit. + // Examples for max_trailing_padding_zeroes_in_precision_mode = 1: + // ToPrecision(230.0, 2) -> "230" + // ToPrecision(230.0, 2) -> "230." with EMIT_TRAILING_DECIMAL_POINT. + // ToPrecision(230.0, 2) -> "2.3e2" with EMIT_TRAILING_ZERO_AFTER_POINT. + DoubleToStringConverter(int flags, + const char* infinity_symbol, + const char* nan_symbol, + char exponent_character, + int decimal_in_shortest_low, + int decimal_in_shortest_high, + int max_leading_padding_zeroes_in_precision_mode, + int max_trailing_padding_zeroes_in_precision_mode) + : flags_(flags), + infinity_symbol_(infinity_symbol), + nan_symbol_(nan_symbol), + exponent_character_(exponent_character), + decimal_in_shortest_low_(decimal_in_shortest_low), + decimal_in_shortest_high_(decimal_in_shortest_high), + max_leading_padding_zeroes_in_precision_mode_( + max_leading_padding_zeroes_in_precision_mode), + max_trailing_padding_zeroes_in_precision_mode_( + max_trailing_padding_zeroes_in_precision_mode) { + // When 'trailing zero after the point' is set, then 'trailing point' + // must be set too. + ASSERT(((flags & EMIT_TRAILING_DECIMAL_POINT) != 0) || + !((flags & EMIT_TRAILING_ZERO_AFTER_POINT) != 0)); + } + + // Returns a converter following the EcmaScript specification. + static const DoubleToStringConverter& EcmaScriptConverter(); + + // Computes the shortest string of digits that correctly represent the input + // number. Depending on decimal_in_shortest_low and decimal_in_shortest_high + // (see constructor) it then either returns a decimal representation, or an + // exponential representation. + // Example with decimal_in_shortest_low = -6, + // decimal_in_shortest_high = 21, + // EMIT_POSITIVE_EXPONENT_SIGN activated, and + // EMIT_TRAILING_DECIMAL_POINT deactived: + // ToShortest(0.000001) -> "0.000001" + // ToShortest(0.0000001) -> "1e-7" + // ToShortest(111111111111111111111.0) -> "111111111111111110000" + // ToShortest(100000000000000000000.0) -> "100000000000000000000" + // ToShortest(1111111111111111111111.0) -> "1.1111111111111111e+21" + // + // Note: the conversion may round the output if the returned string + // is accurate enough to uniquely identify the input-number. + // For example the most precise representation of the double 9e59 equals + // "899999999999999918767229449717619953810131273674690656206848", but + // the converter will return the shorter (but still correct) "9e59". + // + // Returns true if the conversion succeeds. The conversion always succeeds + // except when the input value is special and no infinity_symbol or + // nan_symbol has been given to the constructor. + bool ToShortest(double value, StringBuilder* result_builder) const; + + + // Computes a decimal representation with a fixed number of digits after the + // decimal point. The last emitted digit is rounded. + // + // Examples: + // ToFixed(3.12, 1) -> "3.1" + // ToFixed(3.1415, 3) -> "3.142" + // ToFixed(1234.56789, 4) -> "1234.5679" + // ToFixed(1.23, 5) -> "1.23000" + // ToFixed(0.1, 4) -> "0.1000" + // ToFixed(1e30, 2) -> "1000000000000000019884624838656.00" + // ToFixed(0.1, 30) -> "0.100000000000000005551115123126" + // ToFixed(0.1, 17) -> "0.10000000000000001" + // + // If requested_digits equals 0, then the tail of the result depends on + // the EMIT_TRAILING_DECIMAL_POINT and EMIT_TRAILING_ZERO_AFTER_POINT. + // Examples, for requested_digits == 0, + // let EMIT_TRAILING_DECIMAL_POINT and EMIT_TRAILING_ZERO_AFTER_POINT be + // - false and false: then 123.45 -> 123 + // 0.678 -> 1 + // - true and false: then 123.45 -> 123. + // 0.678 -> 1. + // - true and true: then 123.45 -> 123.0 + // 0.678 -> 1.0 + // + // Returns true if the conversion succeeds. The conversion always succeeds + // except for the following cases: + // - the input value is special and no infinity_symbol or nan_symbol has + // been provided to the constructor, + // - 'value' > 10^kMaxFixedDigitsBeforePoint, or + // - 'requested_digits' > kMaxFixedDigitsAfterPoint. + // The last two conditions imply that the result will never contain more than + // 1 + kMaxFixedDigitsBeforePoint + 1 + kMaxFixedDigitsAfterPoint characters + // (one additional character for the sign, and one for the decimal point). + bool ToFixed(double value, + int requested_digits, + StringBuilder* result_builder) const; + + // Computes a representation in exponential format with requested_digits + // after the decimal point. The last emitted digit is rounded. + // If requested_digits equals -1, then the shortest exponential representation + // is computed. + // + // Examples with EMIT_POSITIVE_EXPONENT_SIGN deactivated, and + // exponent_character set to 'e'. + // ToExponential(3.12, 1) -> "3.1e0" + // ToExponential(5.0, 3) -> "5.000e0" + // ToExponential(0.001, 2) -> "1.00e-3" + // ToExponential(3.1415, -1) -> "3.1415e0" + // ToExponential(3.1415, 4) -> "3.1415e0" + // ToExponential(3.1415, 3) -> "3.142e0" + // ToExponential(123456789000000, 3) -> "1.235e14" + // ToExponential(1000000000000000019884624838656.0, -1) -> "1e30" + // ToExponential(1000000000000000019884624838656.0, 32) -> + // "1.00000000000000001988462483865600e30" + // ToExponential(1234, 0) -> "1e3" + // + // Returns true if the conversion succeeds. The conversion always succeeds + // except for the following cases: + // - the input value is special and no infinity_symbol or nan_symbol has + // been provided to the constructor, + // - 'requested_digits' > kMaxExponentialDigits. + // The last condition implies that the result will never contain more than + // kMaxExponentialDigits + 8 characters (the sign, the digit before the + // decimal point, the decimal point, the exponent character, the + // exponent's sign, and at most 3 exponent digits). + bool ToExponential(double value, + int requested_digits, + StringBuilder* result_builder) const; + + // Computes 'precision' leading digits of the given 'value' and returns them + // either in exponential or decimal format, depending on + // max_{leading|trailing}_padding_zeroes_in_precision_mode (given to the + // constructor). + // The last computed digit is rounded. + // + // Example with max_leading_padding_zeroes_in_precision_mode = 6. + // ToPrecision(0.0000012345, 2) -> "0.0000012" + // ToPrecision(0.00000012345, 2) -> "1.2e-7" + // Similarily the converter may add up to + // max_trailing_padding_zeroes_in_precision_mode in precision mode to avoid + // returning an exponential representation. A zero added by the + // EMIT_TRAILING_ZERO_AFTER_POINT flag is counted for this limit. + // Examples for max_trailing_padding_zeroes_in_precision_mode = 1: + // ToPrecision(230.0, 2) -> "230" + // ToPrecision(230.0, 2) -> "230." with EMIT_TRAILING_DECIMAL_POINT. + // ToPrecision(230.0, 2) -> "2.3e2" with EMIT_TRAILING_ZERO_AFTER_POINT. + // Examples for max_trailing_padding_zeroes_in_precision_mode = 3, and no + // EMIT_TRAILING_ZERO_AFTER_POINT: + // ToPrecision(123450.0, 6) -> "123450" + // ToPrecision(123450.0, 5) -> "123450" + // ToPrecision(123450.0, 4) -> "123500" + // ToPrecision(123450.0, 3) -> "123000" + // ToPrecision(123450.0, 2) -> "1.2e5" + // + // Returns true if the conversion succeeds. The conversion always succeeds + // except for the following cases: + // - the input value is special and no infinity_symbol or nan_symbol has + // been provided to the constructor, + // - precision < kMinPericisionDigits + // - precision > kMaxPrecisionDigits + // The last condition implies that the result will never contain more than + // kMaxPrecisionDigits + 7 characters (the sign, the decimal point, the + // exponent character, the exponent's sign, and at most 3 exponent digits). + bool ToPrecision(double value, + int precision, + StringBuilder* result_builder) const; + + enum DtoaMode { + // Produce the shortest correct representation. + // For example the output of 0.299999999999999988897 is (the less accurate + // but correct) 0.3. + SHORTEST, + // Produce a fixed number of digits after the decimal point. + // For instance fixed(0.1, 4) becomes 0.1000 + // If the input number is big, the output will be big. + FIXED, + // Fixed number of digits (independent of the decimal point). + PRECISION + }; + + // The maximal number of digits that are needed to emit a double in base 10. + // A higher precision can be achieved by using more digits, but the shortest + // accurate representation of any double will never use more digits than + // kBase10MaximalLength. + // Note that DoubleToAscii null-terminates its input. So the given buffer + // should be at least kBase10MaximalLength + 1 characters long. + static const int kBase10MaximalLength = 17; + + // Converts the given double 'v' to ascii. + // The result should be interpreted as buffer * 10^(point-length). + // + // The output depends on the given mode: + // - SHORTEST: produce the least amount of digits for which the internal + // identity requirement is still satisfied. If the digits are printed + // (together with the correct exponent) then reading this number will give + // 'v' again. The buffer will choose the representation that is closest to + // 'v'. If there are two at the same distance, than the one farther away + // from 0 is chosen (halfway cases - ending with 5 - are rounded up). + // In this mode the 'requested_digits' parameter is ignored. + // - FIXED: produces digits necessary to print a given number with + // 'requested_digits' digits after the decimal point. The produced digits + // might be too short in which case the caller has to fill the remainder + // with '0's. + // Example: toFixed(0.001, 5) is allowed to return buffer="1", point=-2. + // Halfway cases are rounded towards +/-Infinity (away from 0). The call + // toFixed(0.15, 2) thus returns buffer="2", point=0. + // The returned buffer may contain digits that would be truncated from the + // shortest representation of the input. + // - PRECISION: produces 'requested_digits' where the first digit is not '0'. + // Even though the length of produced digits usually equals + // 'requested_digits', the function is allowed to return fewer digits, in + // which case the caller has to fill the missing digits with '0's. + // Halfway cases are again rounded away from 0. + // DoubleToAscii expects the given buffer to be big enough to hold all + // digits and a terminating null-character. In SHORTEST-mode it expects a + // buffer of at least kBase10MaximalLength + 1. In all other modes the + // requested_digits parameter (+ 1 for the null-character) limits the size of + // the output. The given length is only used in debug mode to ensure the + // buffer is big enough. + static void DoubleToAscii(double v, + DtoaMode mode, + int requested_digits, + char* buffer, + int buffer_length, + bool* sign, + int* length, + int* point); + + private: + // If the value is a special value (NaN or Infinity) constructs the + // corresponding string using the configured infinity/nan-symbol. + // If either of them is NULL or the value is not special then the + // function returns false. + bool HandleSpecialValues(double value, StringBuilder* result_builder) const; + // Constructs an exponential representation (i.e. 1.234e56). + // The given exponent assumes a decimal point after the first decimal digit. + void CreateExponentialRepresentation(const char* decimal_digits, + int length, + int exponent, + StringBuilder* result_builder) const; + // Creates a decimal representation (i.e 1234.5678). + void CreateDecimalRepresentation(const char* decimal_digits, + int length, + int decimal_point, + int digits_after_point, + StringBuilder* result_builder) const; + + const int flags_; + const char* const infinity_symbol_; + const char* const nan_symbol_; + const char exponent_character_; + const int decimal_in_shortest_low_; + const int decimal_in_shortest_high_; + const int max_leading_padding_zeroes_in_precision_mode_; + const int max_trailing_padding_zeroes_in_precision_mode_; + + DISALLOW_IMPLICIT_CONSTRUCTORS(DoubleToStringConverter); + }; + + + class StringToDoubleConverter { + public: + // Enumeration for allowing octals and ignoring junk when converting + // strings to numbers. + enum Flags { + NO_FLAGS = 0, + ALLOW_HEX = 1, + ALLOW_OCTALS = 2, + ALLOW_TRAILING_JUNK = 4, + ALLOW_LEADING_SPACES = 8, + ALLOW_TRAILING_SPACES = 16, + ALLOW_SPACES_AFTER_SIGN = 32 + }; + + // Flags should be a bit-or combination of the possible Flags-enum. + // - NO_FLAGS: no special flags. + // - ALLOW_HEX: recognizes the prefix "0x". Hex numbers may only be integers. + // Ex: StringToDouble("0x1234") -> 4660.0 + // In StringToDouble("0x1234.56") the characters ".56" are trailing + // junk. The result of the call is hence dependent on + // the ALLOW_TRAILING_JUNK flag and/or the junk value. + // With this flag "0x" is a junk-string. Even with ALLOW_TRAILING_JUNK, + // the string will not be parsed as "0" followed by junk. + // + // - ALLOW_OCTALS: recognizes the prefix "0" for octals: + // If a sequence of octal digits starts with '0', then the number is + // read as octal integer. Octal numbers may only be integers. + // Ex: StringToDouble("01234") -> 668.0 + // StringToDouble("012349") -> 12349.0 // Not a sequence of octal + // // digits. + // In StringToDouble("01234.56") the characters ".56" are trailing + // junk. The result of the call is hence dependent on + // the ALLOW_TRAILING_JUNK flag and/or the junk value. + // In StringToDouble("01234e56") the characters "e56" are trailing + // junk, too. + // - ALLOW_TRAILING_JUNK: ignore trailing characters that are not part of + // a double literal. + // - ALLOW_LEADING_SPACES: skip over leading spaces. + // - ALLOW_TRAILING_SPACES: ignore trailing spaces. + // - ALLOW_SPACES_AFTER_SIGN: ignore spaces after the sign. + // Ex: StringToDouble("- 123.2") -> -123.2. + // StringToDouble("+ 123.2") -> 123.2 + // + // empty_string_value is returned when an empty string is given as input. + // If ALLOW_LEADING_SPACES or ALLOW_TRAILING_SPACES are set, then a string + // containing only spaces is converted to the 'empty_string_value', too. + // + // junk_string_value is returned when + // a) ALLOW_TRAILING_JUNK is not set, and a junk character (a character not + // part of a double-literal) is found. + // b) ALLOW_TRAILING_JUNK is set, but the string does not start with a + // double literal. + // + // infinity_symbol and nan_symbol are strings that are used to detect + // inputs that represent infinity and NaN. They can be null, in which case + // they are ignored. + // The conversion routine first reads any possible signs. Then it compares the + // following character of the input-string with the first character of + // the infinity, and nan-symbol. If either matches, the function assumes, that + // a match has been found, and expects the following input characters to match + // the remaining characters of the special-value symbol. + // This means that the following restrictions apply to special-value symbols: + // - they must not start with signs ('+', or '-'), + // - they must not have the same first character. + // - they must not start with digits. + // + // Examples: + // flags = ALLOW_HEX | ALLOW_TRAILING_JUNK, + // empty_string_value = 0.0, + // junk_string_value = NaN, + // infinity_symbol = "infinity", + // nan_symbol = "nan": + // StringToDouble("0x1234") -> 4660.0. + // StringToDouble("0x1234K") -> 4660.0. + // StringToDouble("") -> 0.0 // empty_string_value. + // StringToDouble(" ") -> NaN // junk_string_value. + // StringToDouble(" 1") -> NaN // junk_string_value. + // StringToDouble("0x") -> NaN // junk_string_value. + // StringToDouble("-123.45") -> -123.45. + // StringToDouble("--123.45") -> NaN // junk_string_value. + // StringToDouble("123e45") -> 123e45. + // StringToDouble("123E45") -> 123e45. + // StringToDouble("123e+45") -> 123e45. + // StringToDouble("123E-45") -> 123e-45. + // StringToDouble("123e") -> 123.0 // trailing junk ignored. + // StringToDouble("123e-") -> 123.0 // trailing junk ignored. + // StringToDouble("+NaN") -> NaN // NaN string literal. + // StringToDouble("-infinity") -> -inf. // infinity literal. + // StringToDouble("Infinity") -> NaN // junk_string_value. + // + // flags = ALLOW_OCTAL | ALLOW_LEADING_SPACES, + // empty_string_value = 0.0, + // junk_string_value = NaN, + // infinity_symbol = NULL, + // nan_symbol = NULL: + // StringToDouble("0x1234") -> NaN // junk_string_value. + // StringToDouble("01234") -> 668.0. + // StringToDouble("") -> 0.0 // empty_string_value. + // StringToDouble(" ") -> 0.0 // empty_string_value. + // StringToDouble(" 1") -> 1.0 + // StringToDouble("0x") -> NaN // junk_string_value. + // StringToDouble("0123e45") -> NaN // junk_string_value. + // StringToDouble("01239E45") -> 1239e45. + // StringToDouble("-infinity") -> NaN // junk_string_value. + // StringToDouble("NaN") -> NaN // junk_string_value. + StringToDoubleConverter(int flags, + double empty_string_value, + double junk_string_value, + const char* infinity_symbol, + const char* nan_symbol) + : flags_(flags), + empty_string_value_(empty_string_value), + junk_string_value_(junk_string_value), + infinity_symbol_(infinity_symbol), + nan_symbol_(nan_symbol) { + } + + // Performs the conversion. + // The output parameter 'processed_characters_count' is set to the number + // of characters that have been processed to read the number. + // Spaces than are processed with ALLOW_{LEADING|TRAILING}_SPACES are included + // in the 'processed_characters_count'. Trailing junk is never included. + double StringToDouble(const char* buffer, + int length, + int* processed_characters_count); + + private: + const int flags_; + const double empty_string_value_; + const double junk_string_value_; + const char* const infinity_symbol_; + const char* const nan_symbol_; + + DISALLOW_IMPLICIT_CONSTRUCTORS(StringToDoubleConverter); + }; + +} // namespace double_conversion + +} // namespace WTF + +#endif // DOUBLE_CONVERSION_DOUBLE_CONVERSION_H_ diff --git a/Source/JavaScriptCore/wtf/dtoa/double.h b/Source/JavaScriptCore/wtf/dtoa/double.h new file mode 100644 index 000000000..0544fdb5a --- /dev/null +++ b/Source/JavaScriptCore/wtf/dtoa/double.h @@ -0,0 +1,249 @@ +// Copyright 2010 the V8 project authors. 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 Google Inc. 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. + +#ifndef DOUBLE_CONVERSION_DOUBLE_H_ +#define DOUBLE_CONVERSION_DOUBLE_H_ + +#include "diy-fp.h" + +namespace WTF { + +namespace double_conversion { + + // We assume that doubles and uint64_t have the same endianness. + static uint64_t double_to_uint64(double d) { return BitCast<uint64_t>(d); } + static double uint64_to_double(uint64_t d64) { return BitCast<double>(d64); } + + // Helper functions for doubles. + class Double { + public: + static const uint64_t kSignMask = UINT64_2PART_C(0x80000000, 00000000); + static const uint64_t kExponentMask = UINT64_2PART_C(0x7FF00000, 00000000); + static const uint64_t kSignificandMask = UINT64_2PART_C(0x000FFFFF, FFFFFFFF); + static const uint64_t kHiddenBit = UINT64_2PART_C(0x00100000, 00000000); + static const int kPhysicalSignificandSize = 52; // Excludes the hidden bit. + static const int kSignificandSize = 53; + + Double() : d64_(0) {} + explicit Double(double d) : d64_(double_to_uint64(d)) {} + explicit Double(uint64_t d64) : d64_(d64) {} + explicit Double(DiyFp diy_fp) + : d64_(DiyFpToUint64(diy_fp)) {} + + // The value encoded by this Double must be greater or equal to +0.0. + // It must not be special (infinity, or NaN). + DiyFp AsDiyFp() const { + ASSERT(Sign() > 0); + ASSERT(!IsSpecial()); + return DiyFp(Significand(), Exponent()); + } + + // The value encoded by this Double must be strictly greater than 0. + DiyFp AsNormalizedDiyFp() const { + ASSERT(value() > 0.0); + uint64_t f = Significand(); + int e = Exponent(); + + // The current double could be a denormal. + while ((f & kHiddenBit) == 0) { + f <<= 1; + e--; + } + // Do the final shifts in one go. + f <<= DiyFp::kSignificandSize - kSignificandSize; + e -= DiyFp::kSignificandSize - kSignificandSize; + return DiyFp(f, e); + } + + // Returns the double's bit as uint64. + uint64_t AsUint64() const { + return d64_; + } + + // Returns the next greater double. Returns +infinity on input +infinity. + double NextDouble() const { + if (d64_ == kInfinity) return Double(kInfinity).value(); + if (Sign() < 0 && Significand() == 0) { + // -0.0 + return 0.0; + } + if (Sign() < 0) { + return Double(d64_ - 1).value(); + } else { + return Double(d64_ + 1).value(); + } + } + + int Exponent() const { + if (IsDenormal()) return kDenormalExponent; + + uint64_t d64 = AsUint64(); + int biased_e = + static_cast<int>((d64 & kExponentMask) >> kPhysicalSignificandSize); + return biased_e - kExponentBias; + } + + uint64_t Significand() const { + uint64_t d64 = AsUint64(); + uint64_t significand = d64 & kSignificandMask; + if (!IsDenormal()) { + return significand + kHiddenBit; + } else { + return significand; + } + } + + // Returns true if the double is a denormal. + bool IsDenormal() const { + uint64_t d64 = AsUint64(); + return (d64 & kExponentMask) == 0; + } + + // We consider denormals not to be special. + // Hence only Infinity and NaN are special. + bool IsSpecial() const { + uint64_t d64 = AsUint64(); + return (d64 & kExponentMask) == kExponentMask; + } + + bool IsNan() const { + uint64_t d64 = AsUint64(); + return ((d64 & kExponentMask) == kExponentMask) && + ((d64 & kSignificandMask) != 0); + } + + bool IsInfinite() const { + uint64_t d64 = AsUint64(); + return ((d64 & kExponentMask) == kExponentMask) && + ((d64 & kSignificandMask) == 0); + } + + int Sign() const { + uint64_t d64 = AsUint64(); + return (d64 & kSignMask) == 0? 1: -1; + } + + // Precondition: the value encoded by this Double must be greater or equal + // than +0.0. + DiyFp UpperBoundary() const { + ASSERT(Sign() > 0); + return DiyFp(Significand() * 2 + 1, Exponent() - 1); + } + + // Computes the two boundaries of this. + // The bigger boundary (m_plus) is normalized. The lower boundary has the same + // exponent as m_plus. + // Precondition: the value encoded by this Double must be greater than 0. + void NormalizedBoundaries(DiyFp* out_m_minus, DiyFp* out_m_plus) const { + ASSERT(value() > 0.0); + DiyFp v = this->AsDiyFp(); + bool significand_is_zero = (v.f() == kHiddenBit); + DiyFp m_plus = DiyFp::Normalize(DiyFp((v.f() << 1) + 1, v.e() - 1)); + DiyFp m_minus; + if (significand_is_zero && v.e() != kDenormalExponent) { + // The boundary is closer. Think of v = 1000e10 and v- = 9999e9. + // Then the boundary (== (v - v-)/2) is not just at a distance of 1e9 but + // at a distance of 1e8. + // The only exception is for the smallest normal: the largest denormal is + // at the same distance as its successor. + // Note: denormals have the same exponent as the smallest normals. + m_minus = DiyFp((v.f() << 2) - 1, v.e() - 2); + } else { + m_minus = DiyFp((v.f() << 1) - 1, v.e() - 1); + } + m_minus.set_f(m_minus.f() << (m_minus.e() - m_plus.e())); + m_minus.set_e(m_plus.e()); + *out_m_plus = m_plus; + *out_m_minus = m_minus; + } + + double value() const { return uint64_to_double(d64_); } + + // Returns the significand size for a given order of magnitude. + // If v = f*2^e with 2^p-1 <= f <= 2^p then p+e is v's order of magnitude. + // This function returns the number of significant binary digits v will have + // once it's encoded into a double. In almost all cases this is equal to + // kSignificandSize. The only exceptions are denormals. They start with + // leading zeroes and their effective significand-size is hence smaller. + static int SignificandSizeForOrderOfMagnitude(int order) { + if (order >= (kDenormalExponent + kSignificandSize)) { + return kSignificandSize; + } + if (order <= kDenormalExponent) return 0; + return order - kDenormalExponent; + } + + static double Infinity() { + return Double(kInfinity).value(); + } + + static double NaN() { + return Double(kNaN).value(); + } + + private: + static const int kExponentBias = 0x3FF + kPhysicalSignificandSize; + static const int kDenormalExponent = -kExponentBias + 1; + static const int kMaxExponent = 0x7FF - kExponentBias; + static const uint64_t kInfinity = UINT64_2PART_C(0x7FF00000, 00000000); + static const uint64_t kNaN = UINT64_2PART_C(0x7FF80000, 00000000); + + const uint64_t d64_; + + static uint64_t DiyFpToUint64(DiyFp diy_fp) { + uint64_t significand = diy_fp.f(); + int exponent = diy_fp.e(); + while (significand > kHiddenBit + kSignificandMask) { + significand >>= 1; + exponent++; + } + if (exponent >= kMaxExponent) { + return kInfinity; + } + if (exponent < kDenormalExponent) { + return 0; + } + while (exponent > kDenormalExponent && (significand & kHiddenBit) == 0) { + significand <<= 1; + exponent--; + } + uint64_t biased_exponent; + if (exponent == kDenormalExponent && (significand & kHiddenBit) == 0) { + biased_exponent = 0; + } else { + biased_exponent = static_cast<uint64_t>(exponent + kExponentBias); + } + return (significand & kSignificandMask) | + (biased_exponent << kPhysicalSignificandSize); + } + }; + +} // namespace double_conversion + +} // namespace WTF + +#endif // DOUBLE_CONVERSION_DOUBLE_H_ diff --git a/Source/JavaScriptCore/wtf/dtoa/fast-dtoa.cc b/Source/JavaScriptCore/wtf/dtoa/fast-dtoa.cc new file mode 100644 index 000000000..9d9872417 --- /dev/null +++ b/Source/JavaScriptCore/wtf/dtoa/fast-dtoa.cc @@ -0,0 +1,741 @@ +// Copyright 2010 the V8 project authors. 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 Google Inc. 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 "config.h" + +#include "fast-dtoa.h" + +#include "cached-powers.h" +#include "diy-fp.h" +#include "double.h" + +namespace WTF { + +namespace double_conversion { + + // The minimal and maximal target exponent define the range of w's binary + // exponent, where 'w' is the result of multiplying the input by a cached power + // of ten. + // + // A different range might be chosen on a different platform, to optimize digit + // generation, but a smaller range requires more powers of ten to be cached. + static const int kMinimalTargetExponent = -60; + static const int kMaximalTargetExponent = -32; + + + // Adjusts the last digit of the generated number, and screens out generated + // solutions that may be inaccurate. A solution may be inaccurate if it is + // outside the safe interval, or if we cannot prove that it is closer to the + // input than a neighboring representation of the same length. + // + // Input: * buffer containing the digits of too_high / 10^kappa + // * the buffer's length + // * distance_too_high_w == (too_high - w).f() * unit + // * unsafe_interval == (too_high - too_low).f() * unit + // * rest = (too_high - buffer * 10^kappa).f() * unit + // * ten_kappa = 10^kappa * unit + // * unit = the common multiplier + // Output: returns true if the buffer is guaranteed to contain the closest + // representable number to the input. + // Modifies the generated digits in the buffer to approach (round towards) w. + static bool RoundWeed(Vector<char> buffer, + int length, + uint64_t distance_too_high_w, + uint64_t unsafe_interval, + uint64_t rest, + uint64_t ten_kappa, + uint64_t unit) { + uint64_t small_distance = distance_too_high_w - unit; + uint64_t big_distance = distance_too_high_w + unit; + // Let w_low = too_high - big_distance, and + // w_high = too_high - small_distance. + // Note: w_low < w < w_high + // + // The real w (* unit) must lie somewhere inside the interval + // ]w_low; w_high[ (often written as "(w_low; w_high)") + + // Basically the buffer currently contains a number in the unsafe interval + // ]too_low; too_high[ with too_low < w < too_high + // + // too_high - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // ^v 1 unit ^ ^ ^ ^ + // boundary_high --------------------- . . . . + // ^v 1 unit . . . . + // - - - - - - - - - - - - - - - - - - - + - - + - - - - - - . . + // . . ^ . . + // . big_distance . . . + // . . . . rest + // small_distance . . . . + // v . . . . + // w_high - - - - - - - - - - - - - - - - - - . . . . + // ^v 1 unit . . . . + // w ---------------------------------------- . . . . + // ^v 1 unit v . . . + // w_low - - - - - - - - - - - - - - - - - - - - - . . . + // . . v + // buffer --------------------------------------------------+-------+-------- + // . . + // safe_interval . + // v . + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - . + // ^v 1 unit . + // boundary_low ------------------------- unsafe_interval + // ^v 1 unit v + // too_low - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // + // + // Note that the value of buffer could lie anywhere inside the range too_low + // to too_high. + // + // boundary_low, boundary_high and w are approximations of the real boundaries + // and v (the input number). They are guaranteed to be precise up to one unit. + // In fact the error is guaranteed to be strictly less than one unit. + // + // Anything that lies outside the unsafe interval is guaranteed not to round + // to v when read again. + // Anything that lies inside the safe interval is guaranteed to round to v + // when read again. + // If the number inside the buffer lies inside the unsafe interval but not + // inside the safe interval then we simply do not know and bail out (returning + // false). + // + // Similarly we have to take into account the imprecision of 'w' when finding + // the closest representation of 'w'. If we have two potential + // representations, and one is closer to both w_low and w_high, then we know + // it is closer to the actual value v. + // + // By generating the digits of too_high we got the largest (closest to + // too_high) buffer that is still in the unsafe interval. In the case where + // w_high < buffer < too_high we try to decrement the buffer. + // This way the buffer approaches (rounds towards) w. + // There are 3 conditions that stop the decrementation process: + // 1) the buffer is already below w_high + // 2) decrementing the buffer would make it leave the unsafe interval + // 3) decrementing the buffer would yield a number below w_high and farther + // away than the current number. In other words: + // (buffer{-1} < w_high) && w_high - buffer{-1} > buffer - w_high + // Instead of using the buffer directly we use its distance to too_high. + // Conceptually rest ~= too_high - buffer + // We need to do the following tests in this order to avoid over- and + // underflows. + ASSERT(rest <= unsafe_interval); + while (rest < small_distance && // Negated condition 1 + unsafe_interval - rest >= ten_kappa && // Negated condition 2 + (rest + ten_kappa < small_distance || // buffer{-1} > w_high + small_distance - rest >= rest + ten_kappa - small_distance)) { + buffer[length - 1]--; + rest += ten_kappa; + } + + // We have approached w+ as much as possible. We now test if approaching w- + // would require changing the buffer. If yes, then we have two possible + // representations close to w, but we cannot decide which one is closer. + if (rest < big_distance && + unsafe_interval - rest >= ten_kappa && + (rest + ten_kappa < big_distance || + big_distance - rest > rest + ten_kappa - big_distance)) { + return false; + } + + // Weeding test. + // The safe interval is [too_low + 2 ulp; too_high - 2 ulp] + // Since too_low = too_high - unsafe_interval this is equivalent to + // [too_high - unsafe_interval + 4 ulp; too_high - 2 ulp] + // Conceptually we have: rest ~= too_high - buffer + return (2 * unit <= rest) && (rest <= unsafe_interval - 4 * unit); + } + + + // Rounds the buffer upwards if the result is closer to v by possibly adding + // 1 to the buffer. If the precision of the calculation is not sufficient to + // round correctly, return false. + // The rounding might shift the whole buffer in which case the kappa is + // adjusted. For example "99", kappa = 3 might become "10", kappa = 4. + // + // If 2*rest > ten_kappa then the buffer needs to be round up. + // rest can have an error of +/- 1 unit. This function accounts for the + // imprecision and returns false, if the rounding direction cannot be + // unambiguously determined. + // + // Precondition: rest < ten_kappa. + static bool RoundWeedCounted(Vector<char> buffer, + int length, + uint64_t rest, + uint64_t ten_kappa, + uint64_t unit, + int* kappa) { + ASSERT(rest < ten_kappa); + // The following tests are done in a specific order to avoid overflows. They + // will work correctly with any uint64 values of rest < ten_kappa and unit. + // + // If the unit is too big, then we don't know which way to round. For example + // a unit of 50 means that the real number lies within rest +/- 50. If + // 10^kappa == 40 then there is no way to tell which way to round. + if (unit >= ten_kappa) return false; + // Even if unit is just half the size of 10^kappa we are already completely + // lost. (And after the previous test we know that the expression will not + // over/underflow.) + if (ten_kappa - unit <= unit) return false; + // If 2 * (rest + unit) <= 10^kappa we can safely round down. + if ((ten_kappa - rest > rest) && (ten_kappa - 2 * rest >= 2 * unit)) { + return true; + } + // If 2 * (rest - unit) >= 10^kappa, then we can safely round up. + if ((rest > unit) && (ten_kappa - (rest - unit) <= (rest - unit))) { + // Increment the last digit recursively until we find a non '9' digit. + buffer[length - 1]++; + for (int i = length - 1; i > 0; --i) { + if (buffer[i] != '0' + 10) break; + buffer[i] = '0'; + buffer[i - 1]++; + } + // If the first digit is now '0'+ 10 we had a buffer with all '9's. With the + // exception of the first digit all digits are now '0'. Simply switch the + // first digit to '1' and adjust the kappa. Example: "99" becomes "10" and + // the power (the kappa) is increased. + if (buffer[0] == '0' + 10) { + buffer[0] = '1'; + (*kappa) += 1; + } + return true; + } + return false; + } + + + static const uint32_t kTen4 = 10000; + static const uint32_t kTen5 = 100000; + static const uint32_t kTen6 = 1000000; + static const uint32_t kTen7 = 10000000; + static const uint32_t kTen8 = 100000000; + static const uint32_t kTen9 = 1000000000; + + // Returns the biggest power of ten that is less than or equal to the given + // number. We furthermore receive the maximum number of bits 'number' has. + // If number_bits == 0 then 0^-1 is returned + // The number of bits must be <= 32. + // Precondition: number < (1 << (number_bits + 1)). + static void BiggestPowerTen(uint32_t number, + int number_bits, + uint32_t* power, + int* exponent) { + ASSERT(number < (uint32_t)(1 << (number_bits + 1))); + + switch (number_bits) { + case 32: + case 31: + case 30: + if (kTen9 <= number) { + *power = kTen9; + *exponent = 9; + break; + } // else fallthrough + case 29: + case 28: + case 27: + if (kTen8 <= number) { + *power = kTen8; + *exponent = 8; + break; + } // else fallthrough + case 26: + case 25: + case 24: + if (kTen7 <= number) { + *power = kTen7; + *exponent = 7; + break; + } // else fallthrough + case 23: + case 22: + case 21: + case 20: + if (kTen6 <= number) { + *power = kTen6; + *exponent = 6; + break; + } // else fallthrough + case 19: + case 18: + case 17: + if (kTen5 <= number) { + *power = kTen5; + *exponent = 5; + break; + } // else fallthrough + case 16: + case 15: + case 14: + if (kTen4 <= number) { + *power = kTen4; + *exponent = 4; + break; + } // else fallthrough + case 13: + case 12: + case 11: + case 10: + if (1000 <= number) { + *power = 1000; + *exponent = 3; + break; + } // else fallthrough + case 9: + case 8: + case 7: + if (100 <= number) { + *power = 100; + *exponent = 2; + break; + } // else fallthrough + case 6: + case 5: + case 4: + if (10 <= number) { + *power = 10; + *exponent = 1; + break; + } // else fallthrough + case 3: + case 2: + case 1: + if (1 <= number) { + *power = 1; + *exponent = 0; + break; + } // else fallthrough + case 0: + *power = 0; + *exponent = -1; + break; + default: + // Following assignments are here to silence compiler warnings. + *power = 0; + *exponent = 0; + UNREACHABLE(); + } + } + + + // Generates the digits of input number w. + // w is a floating-point number (DiyFp), consisting of a significand and an + // exponent. Its exponent is bounded by kMinimalTargetExponent and + // kMaximalTargetExponent. + // Hence -60 <= w.e() <= -32. + // + // Returns false if it fails, in which case the generated digits in the buffer + // should not be used. + // Preconditions: + // * low, w and high are correct up to 1 ulp (unit in the last place). That + // is, their error must be less than a unit of their last digits. + // * low.e() == w.e() == high.e() + // * low < w < high, and taking into account their error: low~ <= high~ + // * kMinimalTargetExponent <= w.e() <= kMaximalTargetExponent + // Postconditions: returns false if procedure fails. + // otherwise: + // * buffer is not null-terminated, but len contains the number of digits. + // * buffer contains the shortest possible decimal digit-sequence + // such that LOW < buffer * 10^kappa < HIGH, where LOW and HIGH are the + // correct values of low and high (without their error). + // * if more than one decimal representation gives the minimal number of + // decimal digits then the one closest to W (where W is the correct value + // of w) is chosen. + // Remark: this procedure takes into account the imprecision of its input + // numbers. If the precision is not enough to guarantee all the postconditions + // then false is returned. This usually happens rarely (~0.5%). + // + // Say, for the sake of example, that + // w.e() == -48, and w.f() == 0x1234567890abcdef + // w's value can be computed by w.f() * 2^w.e() + // We can obtain w's integral digits by simply shifting w.f() by -w.e(). + // -> w's integral part is 0x1234 + // w's fractional part is therefore 0x567890abcdef. + // Printing w's integral part is easy (simply print 0x1234 in decimal). + // In order to print its fraction we repeatedly multiply the fraction by 10 and + // get each digit. Example the first digit after the point would be computed by + // (0x567890abcdef * 10) >> 48. -> 3 + // The whole thing becomes slightly more complicated because we want to stop + // once we have enough digits. That is, once the digits inside the buffer + // represent 'w' we can stop. Everything inside the interval low - high + // represents w. However we have to pay attention to low, high and w's + // imprecision. + static bool DigitGen(DiyFp low, + DiyFp w, + DiyFp high, + Vector<char> buffer, + int* length, + int* kappa) { + ASSERT(low.e() == w.e() && w.e() == high.e()); + ASSERT(low.f() + 1 <= high.f() - 1); + ASSERT(kMinimalTargetExponent <= w.e() && w.e() <= kMaximalTargetExponent); + // low, w and high are imprecise, but by less than one ulp (unit in the last + // place). + // If we remove (resp. add) 1 ulp from low (resp. high) we are certain that + // the new numbers are outside of the interval we want the final + // representation to lie in. + // Inversely adding (resp. removing) 1 ulp from low (resp. high) would yield + // numbers that are certain to lie in the interval. We will use this fact + // later on. + // We will now start by generating the digits within the uncertain + // interval. Later we will weed out representations that lie outside the safe + // interval and thus _might_ lie outside the correct interval. + uint64_t unit = 1; + DiyFp too_low = DiyFp(low.f() - unit, low.e()); + DiyFp too_high = DiyFp(high.f() + unit, high.e()); + // too_low and too_high are guaranteed to lie outside the interval we want the + // generated number in. + DiyFp unsafe_interval = DiyFp::Minus(too_high, too_low); + // We now cut the input number into two parts: the integral digits and the + // fractionals. We will not write any decimal separator though, but adapt + // kappa instead. + // Reminder: we are currently computing the digits (stored inside the buffer) + // such that: too_low < buffer * 10^kappa < too_high + // We use too_high for the digit_generation and stop as soon as possible. + // If we stop early we effectively round down. + DiyFp one = DiyFp(static_cast<uint64_t>(1) << -w.e(), w.e()); + // Division by one is a shift. + uint32_t integrals = static_cast<uint32_t>(too_high.f() >> -one.e()); + // Modulo by one is an and. + uint64_t fractionals = too_high.f() & (one.f() - 1); + uint32_t divisor; + int divisor_exponent; + BiggestPowerTen(integrals, DiyFp::kSignificandSize - (-one.e()), + &divisor, &divisor_exponent); + *kappa = divisor_exponent + 1; + *length = 0; + // Loop invariant: buffer = too_high / 10^kappa (integer division) + // The invariant holds for the first iteration: kappa has been initialized + // with the divisor exponent + 1. And the divisor is the biggest power of ten + // that is smaller than integrals. + while (*kappa > 0) { + int digit = integrals / divisor; + buffer[*length] = '0' + digit; + (*length)++; + integrals %= divisor; + (*kappa)--; + // Note that kappa now equals the exponent of the divisor and that the + // invariant thus holds again. + uint64_t rest = + (static_cast<uint64_t>(integrals) << -one.e()) + fractionals; + // Invariant: too_high = buffer * 10^kappa + DiyFp(rest, one.e()) + // Reminder: unsafe_interval.e() == one.e() + if (rest < unsafe_interval.f()) { + // Rounding down (by not emitting the remaining digits) yields a number + // that lies within the unsafe interval. + return RoundWeed(buffer, *length, DiyFp::Minus(too_high, w).f(), + unsafe_interval.f(), rest, + static_cast<uint64_t>(divisor) << -one.e(), unit); + } + divisor /= 10; + } + + // The integrals have been generated. We are at the point of the decimal + // separator. In the following loop we simply multiply the remaining digits by + // 10 and divide by one. We just need to pay attention to multiply associated + // data (like the interval or 'unit'), too. + // Note that the multiplication by 10 does not overflow, because w.e >= -60 + // and thus one.e >= -60. + ASSERT(one.e() >= -60); + ASSERT(fractionals < one.f()); + ASSERT(UINT64_2PART_C(0xFFFFFFFF, FFFFFFFF) / 10 >= one.f()); + while (true) { + fractionals *= 10; + unit *= 10; + unsafe_interval.set_f(unsafe_interval.f() * 10); + // Integer division by one. + int digit = static_cast<int>(fractionals >> -one.e()); + buffer[*length] = '0' + digit; + (*length)++; + fractionals &= one.f() - 1; // Modulo by one. + (*kappa)--; + if (fractionals < unsafe_interval.f()) { + return RoundWeed(buffer, *length, DiyFp::Minus(too_high, w).f() * unit, + unsafe_interval.f(), fractionals, one.f(), unit); + } + } + } + + + + // Generates (at most) requested_digits digits of input number w. + // w is a floating-point number (DiyFp), consisting of a significand and an + // exponent. Its exponent is bounded by kMinimalTargetExponent and + // kMaximalTargetExponent. + // Hence -60 <= w.e() <= -32. + // + // Returns false if it fails, in which case the generated digits in the buffer + // should not be used. + // Preconditions: + // * w is correct up to 1 ulp (unit in the last place). That + // is, its error must be strictly less than a unit of its last digit. + // * kMinimalTargetExponent <= w.e() <= kMaximalTargetExponent + // + // Postconditions: returns false if procedure fails. + // otherwise: + // * buffer is not null-terminated, but length contains the number of + // digits. + // * the representation in buffer is the most precise representation of + // requested_digits digits. + // * buffer contains at most requested_digits digits of w. If there are less + // than requested_digits digits then some trailing '0's have been removed. + // * kappa is such that + // w = buffer * 10^kappa + eps with |eps| < 10^kappa / 2. + // + // Remark: This procedure takes into account the imprecision of its input + // numbers. If the precision is not enough to guarantee all the postconditions + // then false is returned. This usually happens rarely, but the failure-rate + // increases with higher requested_digits. + static bool DigitGenCounted(DiyFp w, + int requested_digits, + Vector<char> buffer, + int* length, + int* kappa) { + ASSERT(kMinimalTargetExponent <= w.e() && w.e() <= kMaximalTargetExponent); + ASSERT(kMinimalTargetExponent >= -60); + ASSERT(kMaximalTargetExponent <= -32); + // w is assumed to have an error less than 1 unit. Whenever w is scaled we + // also scale its error. + uint64_t w_error = 1; + // We cut the input number into two parts: the integral digits and the + // fractional digits. We don't emit any decimal separator, but adapt kappa + // instead. Example: instead of writing "1.2" we put "12" into the buffer and + // increase kappa by 1. + DiyFp one = DiyFp(static_cast<uint64_t>(1) << -w.e(), w.e()); + // Division by one is a shift. + uint32_t integrals = static_cast<uint32_t>(w.f() >> -one.e()); + // Modulo by one is an and. + uint64_t fractionals = w.f() & (one.f() - 1); + uint32_t divisor; + int divisor_exponent; + BiggestPowerTen(integrals, DiyFp::kSignificandSize - (-one.e()), + &divisor, &divisor_exponent); + *kappa = divisor_exponent + 1; + *length = 0; + + // Loop invariant: buffer = w / 10^kappa (integer division) + // The invariant holds for the first iteration: kappa has been initialized + // with the divisor exponent + 1. And the divisor is the biggest power of ten + // that is smaller than 'integrals'. + while (*kappa > 0) { + int digit = integrals / divisor; + buffer[*length] = '0' + digit; + (*length)++; + requested_digits--; + integrals %= divisor; + (*kappa)--; + // Note that kappa now equals the exponent of the divisor and that the + // invariant thus holds again. + if (requested_digits == 0) break; + divisor /= 10; + } + + if (requested_digits == 0) { + uint64_t rest = + (static_cast<uint64_t>(integrals) << -one.e()) + fractionals; + return RoundWeedCounted(buffer, *length, rest, + static_cast<uint64_t>(divisor) << -one.e(), w_error, + kappa); + } + + // The integrals have been generated. We are at the point of the decimal + // separator. In the following loop we simply multiply the remaining digits by + // 10 and divide by one. We just need to pay attention to multiply associated + // data (the 'unit'), too. + // Note that the multiplication by 10 does not overflow, because w.e >= -60 + // and thus one.e >= -60. + ASSERT(one.e() >= -60); + ASSERT(fractionals < one.f()); + ASSERT(UINT64_2PART_C(0xFFFFFFFF, FFFFFFFF) / 10 >= one.f()); + while (requested_digits > 0 && fractionals > w_error) { + fractionals *= 10; + w_error *= 10; + // Integer division by one. + int digit = static_cast<int>(fractionals >> -one.e()); + buffer[*length] = '0' + digit; + (*length)++; + requested_digits--; + fractionals &= one.f() - 1; // Modulo by one. + (*kappa)--; + } + if (requested_digits != 0) return false; + return RoundWeedCounted(buffer, *length, fractionals, one.f(), w_error, + kappa); + } + + + // Provides a decimal representation of v. + // Returns true if it succeeds, otherwise the result cannot be trusted. + // There will be *length digits inside the buffer (not null-terminated). + // If the function returns true then + // v == (double) (buffer * 10^decimal_exponent). + // The digits in the buffer are the shortest representation possible: no + // 0.09999999999999999 instead of 0.1. The shorter representation will even be + // chosen even if the longer one would be closer to v. + // The last digit will be closest to the actual v. That is, even if several + // digits might correctly yield 'v' when read again, the closest will be + // computed. + static bool Grisu3(double v, + Vector<char> buffer, + int* length, + int* decimal_exponent) { + DiyFp w = Double(v).AsNormalizedDiyFp(); + // boundary_minus and boundary_plus are the boundaries between v and its + // closest floating-point neighbors. Any number strictly between + // boundary_minus and boundary_plus will round to v when convert to a double. + // Grisu3 will never output representations that lie exactly on a boundary. + DiyFp boundary_minus, boundary_plus; + Double(v).NormalizedBoundaries(&boundary_minus, &boundary_plus); + ASSERT(boundary_plus.e() == w.e()); + DiyFp ten_mk; // Cached power of ten: 10^-k + int mk; // -k + int ten_mk_minimal_binary_exponent = + kMinimalTargetExponent - (w.e() + DiyFp::kSignificandSize); + int ten_mk_maximal_binary_exponent = + kMaximalTargetExponent - (w.e() + DiyFp::kSignificandSize); + PowersOfTenCache::GetCachedPowerForBinaryExponentRange( + ten_mk_minimal_binary_exponent, + ten_mk_maximal_binary_exponent, + &ten_mk, &mk); + ASSERT((kMinimalTargetExponent <= w.e() + ten_mk.e() + + DiyFp::kSignificandSize) && + (kMaximalTargetExponent >= w.e() + ten_mk.e() + + DiyFp::kSignificandSize)); + // Note that ten_mk is only an approximation of 10^-k. A DiyFp only contains a + // 64 bit significand and ten_mk is thus only precise up to 64 bits. + + // The DiyFp::Times procedure rounds its result, and ten_mk is approximated + // too. The variable scaled_w (as well as scaled_boundary_minus/plus) are now + // off by a small amount. + // In fact: scaled_w - w*10^k < 1ulp (unit in the last place) of scaled_w. + // In other words: let f = scaled_w.f() and e = scaled_w.e(), then + // (f-1) * 2^e < w*10^k < (f+1) * 2^e + DiyFp scaled_w = DiyFp::Times(w, ten_mk); + ASSERT(scaled_w.e() == + boundary_plus.e() + ten_mk.e() + DiyFp::kSignificandSize); + // In theory it would be possible to avoid some recomputations by computing + // the difference between w and boundary_minus/plus (a power of 2) and to + // compute scaled_boundary_minus/plus by subtracting/adding from + // scaled_w. However the code becomes much less readable and the speed + // enhancements are not terriffic. + DiyFp scaled_boundary_minus = DiyFp::Times(boundary_minus, ten_mk); + DiyFp scaled_boundary_plus = DiyFp::Times(boundary_plus, ten_mk); + + // DigitGen will generate the digits of scaled_w. Therefore we have + // v == (double) (scaled_w * 10^-mk). + // Set decimal_exponent == -mk and pass it to DigitGen. If scaled_w is not an + // integer than it will be updated. For instance if scaled_w == 1.23 then + // the buffer will be filled with "123" und the decimal_exponent will be + // decreased by 2. + int kappa; + bool result = DigitGen(scaled_boundary_minus, scaled_w, scaled_boundary_plus, + buffer, length, &kappa); + *decimal_exponent = -mk + kappa; + return result; + } + + + // The "counted" version of grisu3 (see above) only generates requested_digits + // number of digits. This version does not generate the shortest representation, + // and with enough requested digits 0.1 will at some point print as 0.9999999... + // Grisu3 is too imprecise for real halfway cases (1.5 will not work) and + // therefore the rounding strategy for halfway cases is irrelevant. + static bool Grisu3Counted(double v, + int requested_digits, + Vector<char> buffer, + int* length, + int* decimal_exponent) { + DiyFp w = Double(v).AsNormalizedDiyFp(); + DiyFp ten_mk; // Cached power of ten: 10^-k + int mk; // -k + int ten_mk_minimal_binary_exponent = + kMinimalTargetExponent - (w.e() + DiyFp::kSignificandSize); + int ten_mk_maximal_binary_exponent = + kMaximalTargetExponent - (w.e() + DiyFp::kSignificandSize); + PowersOfTenCache::GetCachedPowerForBinaryExponentRange( + ten_mk_minimal_binary_exponent, + ten_mk_maximal_binary_exponent, + &ten_mk, &mk); + ASSERT((kMinimalTargetExponent <= w.e() + ten_mk.e() + + DiyFp::kSignificandSize) && + (kMaximalTargetExponent >= w.e() + ten_mk.e() + + DiyFp::kSignificandSize)); + // Note that ten_mk is only an approximation of 10^-k. A DiyFp only contains a + // 64 bit significand and ten_mk is thus only precise up to 64 bits. + + // The DiyFp::Times procedure rounds its result, and ten_mk is approximated + // too. The variable scaled_w (as well as scaled_boundary_minus/plus) are now + // off by a small amount. + // In fact: scaled_w - w*10^k < 1ulp (unit in the last place) of scaled_w. + // In other words: let f = scaled_w.f() and e = scaled_w.e(), then + // (f-1) * 2^e < w*10^k < (f+1) * 2^e + DiyFp scaled_w = DiyFp::Times(w, ten_mk); + + // We now have (double) (scaled_w * 10^-mk). + // DigitGen will generate the first requested_digits digits of scaled_w and + // return together with a kappa such that scaled_w ~= buffer * 10^kappa. (It + // will not always be exactly the same since DigitGenCounted only produces a + // limited number of digits.) + int kappa; + bool result = DigitGenCounted(scaled_w, requested_digits, + buffer, length, &kappa); + *decimal_exponent = -mk + kappa; + return result; + } + + + bool FastDtoa(double v, + FastDtoaMode mode, + int requested_digits, + Vector<char> buffer, + int* length, + int* decimal_point) { + ASSERT(v > 0); + ASSERT(!Double(v).IsSpecial()); + + bool result = false; + int decimal_exponent = 0; + switch (mode) { + case FAST_DTOA_SHORTEST: + result = Grisu3(v, buffer, length, &decimal_exponent); + break; + case FAST_DTOA_PRECISION: + result = Grisu3Counted(v, requested_digits, + buffer, length, &decimal_exponent); + break; + default: + UNREACHABLE(); + } + if (result) { + *decimal_point = *length + decimal_exponent; + buffer[*length] = '\0'; + } + return result; + } + +} // namespace double_conversion + +} // namespace WTF diff --git a/Source/JavaScriptCore/wtf/dtoa/fast-dtoa.h b/Source/JavaScriptCore/wtf/dtoa/fast-dtoa.h new file mode 100644 index 000000000..876a9f382 --- /dev/null +++ b/Source/JavaScriptCore/wtf/dtoa/fast-dtoa.h @@ -0,0 +1,88 @@ +// Copyright 2010 the V8 project authors. 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 Google Inc. 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. + +#ifndef DOUBLE_CONVERSION_FAST_DTOA_H_ +#define DOUBLE_CONVERSION_FAST_DTOA_H_ + +#include "utils.h" + +namespace WTF { + +namespace double_conversion { + + enum FastDtoaMode { + // Computes the shortest representation of the given input. The returned + // result will be the most accurate number of this length. Longer + // representations might be more accurate. + FAST_DTOA_SHORTEST, + // Computes a representation where the precision (number of digits) is + // given as input. The precision is independent of the decimal point. + FAST_DTOA_PRECISION + }; + + // FastDtoa will produce at most kFastDtoaMaximalLength digits. This does not + // include the terminating '\0' character. + static const int kFastDtoaMaximalLength = 17; + + // Provides a decimal representation of v. + // The result should be interpreted as buffer * 10^(point - length). + // + // Precondition: + // * v must be a strictly positive finite double. + // + // Returns true if it succeeds, otherwise the result can not be trusted. + // There will be *length digits inside the buffer followed by a null terminator. + // If the function returns true and mode equals + // - FAST_DTOA_SHORTEST, then + // the parameter requested_digits is ignored. + // The result satisfies + // v == (double) (buffer * 10^(point - length)). + // The digits in the buffer are the shortest representation possible. E.g. + // if 0.099999999999 and 0.1 represent the same double then "1" is returned + // with point = 0. + // The last digit will be closest to the actual v. That is, even if several + // digits might correctly yield 'v' when read again, the buffer will contain + // the one closest to v. + // - FAST_DTOA_PRECISION, then + // the buffer contains requested_digits digits. + // the difference v - (buffer * 10^(point-length)) is closest to zero for + // all possible representations of requested_digits digits. + // If there are two values that are equally close, then FastDtoa returns + // false. + // For both modes the buffer must be large enough to hold the result. + bool FastDtoa(double d, + FastDtoaMode mode, + int requested_digits, + Vector<char> buffer, + int* length, + int* decimal_point); + +} // namespace double_conversion + +} // namespace WTF + +#endif // DOUBLE_CONVERSION_FAST_DTOA_H_ diff --git a/Source/JavaScriptCore/wtf/dtoa/fixed-dtoa.cc b/Source/JavaScriptCore/wtf/dtoa/fixed-dtoa.cc new file mode 100644 index 000000000..40b7180e5 --- /dev/null +++ b/Source/JavaScriptCore/wtf/dtoa/fixed-dtoa.cc @@ -0,0 +1,410 @@ +// Copyright 2010 the V8 project authors. 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 Google Inc. 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 "config.h" + +#include <math.h> + +#include "UnusedParam.h" +#include "fixed-dtoa.h" +#include "double.h" + +namespace WTF { + +namespace double_conversion { + + // Represents a 128bit type. This class should be replaced by a native type on + // platforms that support 128bit integers. + class UInt128 { + public: + UInt128() : high_bits_(0), low_bits_(0) { } + UInt128(uint64_t high, uint64_t low) : high_bits_(high), low_bits_(low) { } + + void Multiply(uint32_t multiplicand) { + uint64_t accumulator; + + accumulator = (low_bits_ & kMask32) * multiplicand; + uint32_t part = static_cast<uint32_t>(accumulator & kMask32); + accumulator >>= 32; + accumulator = accumulator + (low_bits_ >> 32) * multiplicand; + low_bits_ = (accumulator << 32) + part; + accumulator >>= 32; + accumulator = accumulator + (high_bits_ & kMask32) * multiplicand; + part = static_cast<uint32_t>(accumulator & kMask32); + accumulator >>= 32; + accumulator = accumulator + (high_bits_ >> 32) * multiplicand; + high_bits_ = (accumulator << 32) + part; + ASSERT((accumulator >> 32) == 0); + } + + void Shift(int shift_amount) { + ASSERT(-64 <= shift_amount && shift_amount <= 64); + if (shift_amount == 0) { + return; + } else if (shift_amount == -64) { + high_bits_ = low_bits_; + low_bits_ = 0; + } else if (shift_amount == 64) { + low_bits_ = high_bits_; + high_bits_ = 0; + } else if (shift_amount <= 0) { + high_bits_ <<= -shift_amount; + high_bits_ += low_bits_ >> (64 + shift_amount); + low_bits_ <<= -shift_amount; + } else { + low_bits_ >>= shift_amount; + low_bits_ += high_bits_ << (64 - shift_amount); + high_bits_ >>= shift_amount; + } + } + + // Modifies *this to *this MOD (2^power). + // Returns *this DIV (2^power). + int DivModPowerOf2(int power) { + if (power >= 64) { + int result = static_cast<int>(high_bits_ >> (power - 64)); + high_bits_ -= static_cast<uint64_t>(result) << (power - 64); + return result; + } else { + uint64_t part_low = low_bits_ >> power; + uint64_t part_high = high_bits_ << (64 - power); + int result = static_cast<int>(part_low + part_high); + high_bits_ = 0; + low_bits_ -= part_low << power; + return result; + } + } + + bool IsZero() const { + return high_bits_ == 0 && low_bits_ == 0; + } + + int BitAt(int position) { + if (position >= 64) { + return static_cast<int>(high_bits_ >> (position - 64)) & 1; + } else { + return static_cast<int>(low_bits_ >> position) & 1; + } + } + + private: + static const uint64_t kMask32 = 0xFFFFFFFF; + // Value == (high_bits_ << 64) + low_bits_ + uint64_t high_bits_; + uint64_t low_bits_; + }; + + + static const int kDoubleSignificandSize = 53; // Includes the hidden bit. + + + static void FillDigits32FixedLength(uint32_t number, int requested_length, + Vector<char> buffer, int* length) { + for (int i = requested_length - 1; i >= 0; --i) { + buffer[(*length) + i] = '0' + number % 10; + number /= 10; + } + *length += requested_length; + } + + + static void FillDigits32(uint32_t number, Vector<char> buffer, int* length) { + int number_length = 0; + // We fill the digits in reverse order and exchange them afterwards. + while (number != 0) { + int digit = number % 10; + number /= 10; + buffer[(*length) + number_length] = '0' + digit; + number_length++; + } + // Exchange the digits. + int i = *length; + int j = *length + number_length - 1; + while (i < j) { + char tmp = buffer[i]; + buffer[i] = buffer[j]; + buffer[j] = tmp; + i++; + j--; + } + *length += number_length; + } + + + static void FillDigits64FixedLength(uint64_t number, int requested_length, + Vector<char> buffer, int* length) { + UNUSED_PARAM(requested_length); + const uint32_t kTen7 = 10000000; + // For efficiency cut the number into 3 uint32_t parts, and print those. + uint32_t part2 = static_cast<uint32_t>(number % kTen7); + number /= kTen7; + uint32_t part1 = static_cast<uint32_t>(number % kTen7); + uint32_t part0 = static_cast<uint32_t>(number / kTen7); + + FillDigits32FixedLength(part0, 3, buffer, length); + FillDigits32FixedLength(part1, 7, buffer, length); + FillDigits32FixedLength(part2, 7, buffer, length); + } + + + static void FillDigits64(uint64_t number, Vector<char> buffer, int* length) { + const uint32_t kTen7 = 10000000; + // For efficiency cut the number into 3 uint32_t parts, and print those. + uint32_t part2 = static_cast<uint32_t>(number % kTen7); + number /= kTen7; + uint32_t part1 = static_cast<uint32_t>(number % kTen7); + uint32_t part0 = static_cast<uint32_t>(number / kTen7); + + if (part0 != 0) { + FillDigits32(part0, buffer, length); + FillDigits32FixedLength(part1, 7, buffer, length); + FillDigits32FixedLength(part2, 7, buffer, length); + } else if (part1 != 0) { + FillDigits32(part1, buffer, length); + FillDigits32FixedLength(part2, 7, buffer, length); + } else { + FillDigits32(part2, buffer, length); + } + } + + + static void RoundUp(Vector<char> buffer, int* length, int* decimal_point) { + // An empty buffer represents 0. + if (*length == 0) { + buffer[0] = '1'; + *decimal_point = 1; + *length = 1; + return; + } + // Round the last digit until we either have a digit that was not '9' or until + // we reached the first digit. + buffer[(*length) - 1]++; + for (int i = (*length) - 1; i > 0; --i) { + if (buffer[i] != '0' + 10) { + return; + } + buffer[i] = '0'; + buffer[i - 1]++; + } + // If the first digit is now '0' + 10, we would need to set it to '0' and add + // a '1' in front. However we reach the first digit only if all following + // digits had been '9' before rounding up. Now all trailing digits are '0' and + // we simply switch the first digit to '1' and update the decimal-point + // (indicating that the point is now one digit to the right). + if (buffer[0] == '0' + 10) { + buffer[0] = '1'; + (*decimal_point)++; + } + } + + + // The given fractionals number represents a fixed-point number with binary + // point at bit (-exponent). + // Preconditions: + // -128 <= exponent <= 0. + // 0 <= fractionals * 2^exponent < 1 + // The buffer holds the result. + // The function will round its result. During the rounding-process digits not + // generated by this function might be updated, and the decimal-point variable + // might be updated. If this function generates the digits 99 and the buffer + // already contained "199" (thus yielding a buffer of "19999") then a + // rounding-up will change the contents of the buffer to "20000". + static void FillFractionals(uint64_t fractionals, int exponent, + int fractional_count, Vector<char> buffer, + int* length, int* decimal_point) { + ASSERT(-128 <= exponent && exponent <= 0); + // 'fractionals' is a fixed-point number, with binary point at bit + // (-exponent). Inside the function the non-converted remainder of fractionals + // is a fixed-point number, with binary point at bit 'point'. + if (-exponent <= 64) { + // One 64 bit number is sufficient. + ASSERT(fractionals >> 56 == 0); + int point = -exponent; + for (int i = 0; i < fractional_count; ++i) { + if (fractionals == 0) break; + // Instead of multiplying by 10 we multiply by 5 and adjust the point + // location. This way the fractionals variable will not overflow. + // Invariant at the beginning of the loop: fractionals < 2^point. + // Initially we have: point <= 64 and fractionals < 2^56 + // After each iteration the point is decremented by one. + // Note that 5^3 = 125 < 128 = 2^7. + // Therefore three iterations of this loop will not overflow fractionals + // (even without the subtraction at the end of the loop body). At this + // time point will satisfy point <= 61 and therefore fractionals < 2^point + // and any further multiplication of fractionals by 5 will not overflow. + fractionals *= 5; + point--; + int digit = static_cast<int>(fractionals >> point); + buffer[*length] = '0' + digit; + (*length)++; + fractionals -= static_cast<uint64_t>(digit) << point; + } + // If the first bit after the point is set we have to round up. + if (((fractionals >> (point - 1)) & 1) == 1) { + RoundUp(buffer, length, decimal_point); + } + } else { // We need 128 bits. + ASSERT(64 < -exponent && -exponent <= 128); + UInt128 fractionals128 = UInt128(fractionals, 0); + fractionals128.Shift(-exponent - 64); + int point = 128; + for (int i = 0; i < fractional_count; ++i) { + if (fractionals128.IsZero()) break; + // As before: instead of multiplying by 10 we multiply by 5 and adjust the + // point location. + // This multiplication will not overflow for the same reasons as before. + fractionals128.Multiply(5); + point--; + int digit = fractionals128.DivModPowerOf2(point); + buffer[*length] = '0' + digit; + (*length)++; + } + if (fractionals128.BitAt(point - 1) == 1) { + RoundUp(buffer, length, decimal_point); + } + } + } + + + // Removes leading and trailing zeros. + // If leading zeros are removed then the decimal point position is adjusted. + static void TrimZeros(Vector<char> buffer, int* length, int* decimal_point) { + while (*length > 0 && buffer[(*length) - 1] == '0') { + (*length)--; + } + int first_non_zero = 0; + while (first_non_zero < *length && buffer[first_non_zero] == '0') { + first_non_zero++; + } + if (first_non_zero != 0) { + for (int i = first_non_zero; i < *length; ++i) { + buffer[i - first_non_zero] = buffer[i]; + } + *length -= first_non_zero; + *decimal_point -= first_non_zero; + } + } + + + bool FastFixedDtoa(double v, + int fractional_count, + Vector<char> buffer, + int* length, + int* decimal_point) { + const uint32_t kMaxUInt32 = 0xFFFFFFFF; + uint64_t significand = Double(v).Significand(); + int exponent = Double(v).Exponent(); + // v = significand * 2^exponent (with significand a 53bit integer). + // If the exponent is larger than 20 (i.e. we may have a 73bit number) then we + // don't know how to compute the representation. 2^73 ~= 9.5*10^21. + // If necessary this limit could probably be increased, but we don't need + // more. + if (exponent > 20) return false; + if (fractional_count > 20) return false; + *length = 0; + // At most kDoubleSignificandSize bits of the significand are non-zero. + // Given a 64 bit integer we have 11 0s followed by 53 potentially non-zero + // bits: 0..11*..0xxx..53*..xx + if (exponent + kDoubleSignificandSize > 64) { + // The exponent must be > 11. + // + // We know that v = significand * 2^exponent. + // And the exponent > 11. + // We simplify the task by dividing v by 10^17. + // The quotient delivers the first digits, and the remainder fits into a 64 + // bit number. + // Dividing by 10^17 is equivalent to dividing by 5^17*2^17. + const uint64_t kFive17 = UINT64_2PART_C(0xB1, A2BC2EC5); // 5^17 + uint64_t divisor = kFive17; + int divisor_power = 17; + uint64_t dividend = significand; + uint32_t quotient; + uint64_t remainder; + // Let v = f * 2^e with f == significand and e == exponent. + // Then need q (quotient) and r (remainder) as follows: + // v = q * 10^17 + r + // f * 2^e = q * 10^17 + r + // f * 2^e = q * 5^17 * 2^17 + r + // If e > 17 then + // f * 2^(e-17) = q * 5^17 + r/2^17 + // else + // f = q * 5^17 * 2^(17-e) + r/2^e + if (exponent > divisor_power) { + // We only allow exponents of up to 20 and therefore (17 - e) <= 3 + dividend <<= exponent - divisor_power; + quotient = static_cast<uint32_t>(dividend / divisor); + remainder = (dividend % divisor) << divisor_power; + } else { + divisor <<= divisor_power - exponent; + quotient = static_cast<uint32_t>(dividend / divisor); + remainder = (dividend % divisor) << exponent; + } + FillDigits32(quotient, buffer, length); + FillDigits64FixedLength(remainder, divisor_power, buffer, length); + *decimal_point = *length; + } else if (exponent >= 0) { + // 0 <= exponent <= 11 + significand <<= exponent; + FillDigits64(significand, buffer, length); + *decimal_point = *length; + } else if (exponent > -kDoubleSignificandSize) { + // We have to cut the number. + uint64_t integrals = significand >> -exponent; + uint64_t fractionals = significand - (integrals << -exponent); + if (integrals > kMaxUInt32) { + FillDigits64(integrals, buffer, length); + } else { + FillDigits32(static_cast<uint32_t>(integrals), buffer, length); + } + *decimal_point = *length; + FillFractionals(fractionals, exponent, fractional_count, + buffer, length, decimal_point); + } else if (exponent < -128) { + // This configuration (with at most 20 digits) means that all digits must be + // 0. + ASSERT(fractional_count <= 20); + buffer[0] = '\0'; + *length = 0; + *decimal_point = -fractional_count; + } else { + *decimal_point = 0; + FillFractionals(significand, exponent, fractional_count, + buffer, length, decimal_point); + } + TrimZeros(buffer, length, decimal_point); + buffer[*length] = '\0'; + if ((*length) == 0) { + // The string is empty and the decimal_point thus has no importance. Mimick + // Gay's dtoa and and set it to -fractional_count. + *decimal_point = -fractional_count; + } + return true; + } + +} // namespace double_conversion + +} // namespace WTF diff --git a/Source/JavaScriptCore/wtf/dtoa/fixed-dtoa.h b/Source/JavaScriptCore/wtf/dtoa/fixed-dtoa.h new file mode 100644 index 000000000..8c0adb758 --- /dev/null +++ b/Source/JavaScriptCore/wtf/dtoa/fixed-dtoa.h @@ -0,0 +1,60 @@ +// Copyright 2010 the V8 project authors. 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 Google Inc. 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. + +#ifndef DOUBLE_CONVERSION_FIXED_DTOA_H_ +#define DOUBLE_CONVERSION_FIXED_DTOA_H_ + +#include "utils.h" + +namespace WTF { + +namespace double_conversion { + + // Produces digits necessary to print a given number with + // 'fractional_count' digits after the decimal point. + // The buffer must be big enough to hold the result plus one terminating null + // character. + // + // The produced digits might be too short in which case the caller has to fill + // the gaps with '0's. + // Example: FastFixedDtoa(0.001, 5, ...) is allowed to return buffer = "1", and + // decimal_point = -2. + // Halfway cases are rounded towards +/-Infinity (away from 0). The call + // FastFixedDtoa(0.15, 2, ...) thus returns buffer = "2", decimal_point = 0. + // The returned buffer may contain digits that would be truncated from the + // shortest representation of the input. + // + // This method only works for some parameters. If it can't handle the input it + // returns false. The output is null-terminated when the function succeeds. + bool FastFixedDtoa(double v, int fractional_count, + Vector<char> buffer, int* length, int* decimal_point); + +} // namespace double_conversion + +} // namespace WTF + +#endif // DOUBLE_CONVERSION_FIXED_DTOA_H_ diff --git a/Source/JavaScriptCore/wtf/dtoa/strtod.cc b/Source/JavaScriptCore/wtf/dtoa/strtod.cc new file mode 100644 index 000000000..477e7158c --- /dev/null +++ b/Source/JavaScriptCore/wtf/dtoa/strtod.cc @@ -0,0 +1,447 @@ +// Copyright 2010 the V8 project authors. 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 Google Inc. 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 "config.h" + +#include <stdarg.h> +#include <limits.h> + +#include "strtod.h" +#include "bignum.h" +#include "cached-powers.h" +#include "double.h" + +namespace WTF { + +namespace double_conversion { + + // 2^53 = 9007199254740992. + // Any integer with at most 15 decimal digits will hence fit into a double + // (which has a 53bit significand) without loss of precision. + static const int kMaxExactDoubleIntegerDecimalDigits = 15; + // 2^64 = 18446744073709551616 > 10^19 + static const int kMaxUint64DecimalDigits = 19; + + // Max double: 1.7976931348623157 x 10^308 + // Min non-zero double: 4.9406564584124654 x 10^-324 + // Any x >= 10^309 is interpreted as +infinity. + // Any x <= 10^-324 is interpreted as 0. + // Note that 2.5e-324 (despite being smaller than the min double) will be read + // as non-zero (equal to the min non-zero double). + static const int kMaxDecimalPower = 309; + static const int kMinDecimalPower = -324; + + // 2^64 = 18446744073709551616 + static const uint64_t kMaxUint64 = UINT64_2PART_C(0xFFFFFFFF, FFFFFFFF); + + + static const double exact_powers_of_ten[] = { + 1.0, // 10^0 + 10.0, + 100.0, + 1000.0, + 10000.0, + 100000.0, + 1000000.0, + 10000000.0, + 100000000.0, + 1000000000.0, + 10000000000.0, // 10^10 + 100000000000.0, + 1000000000000.0, + 10000000000000.0, + 100000000000000.0, + 1000000000000000.0, + 10000000000000000.0, + 100000000000000000.0, + 1000000000000000000.0, + 10000000000000000000.0, + 100000000000000000000.0, // 10^20 + 1000000000000000000000.0, + // 10^22 = 0x21e19e0c9bab2400000 = 0x878678326eac9 * 2^22 + 10000000000000000000000.0 + }; + static const int kExactPowersOfTenSize = ARRAY_SIZE(exact_powers_of_ten); + + // Maximum number of significant digits in the decimal representation. + // In fact the value is 772 (see conversions.cc), but to give us some margin + // we round up to 780. + static const int kMaxSignificantDecimalDigits = 780; + + static Vector<const char> TrimLeadingZeros(Vector<const char> buffer) { + for (int i = 0; i < buffer.length(); i++) { + if (buffer[i] != '0') { + return buffer.SubVector(i, buffer.length()); + } + } + return Vector<const char>(buffer.start(), 0); + } + + + static Vector<const char> TrimTrailingZeros(Vector<const char> buffer) { + for (int i = buffer.length() - 1; i >= 0; --i) { + if (buffer[i] != '0') { + return buffer.SubVector(0, i + 1); + } + } + return Vector<const char>(buffer.start(), 0); + } + + + static void TrimToMaxSignificantDigits(Vector<const char> buffer, + int exponent, + char* significant_buffer, + int* significant_exponent) { + for (int i = 0; i < kMaxSignificantDecimalDigits - 1; ++i) { + significant_buffer[i] = buffer[i]; + } + // The input buffer has been trimmed. Therefore the last digit must be + // different from '0'. + ASSERT(buffer[buffer.length() - 1] != '0'); + // Set the last digit to be non-zero. This is sufficient to guarantee + // correct rounding. + significant_buffer[kMaxSignificantDecimalDigits - 1] = '1'; + *significant_exponent = + exponent + (buffer.length() - kMaxSignificantDecimalDigits); + } + + // Reads digits from the buffer and converts them to a uint64. + // Reads in as many digits as fit into a uint64. + // When the string starts with "1844674407370955161" no further digit is read. + // Since 2^64 = 18446744073709551616 it would still be possible read another + // digit if it was less or equal than 6, but this would complicate the code. + static uint64_t ReadUint64(Vector<const char> buffer, + int* number_of_read_digits) { + uint64_t result = 0; + int i = 0; + while (i < buffer.length() && result <= (kMaxUint64 / 10 - 1)) { + int digit = buffer[i++] - '0'; + ASSERT(0 <= digit && digit <= 9); + result = 10 * result + digit; + } + *number_of_read_digits = i; + return result; + } + + + // Reads a DiyFp from the buffer. + // The returned DiyFp is not necessarily normalized. + // If remaining_decimals is zero then the returned DiyFp is accurate. + // Otherwise it has been rounded and has error of at most 1/2 ulp. + static void ReadDiyFp(Vector<const char> buffer, + DiyFp* result, + int* remaining_decimals) { + int read_digits; + uint64_t significand = ReadUint64(buffer, &read_digits); + if (buffer.length() == read_digits) { + *result = DiyFp(significand, 0); + *remaining_decimals = 0; + } else { + // Round the significand. + if (buffer[read_digits] >= '5') { + significand++; + } + // Compute the binary exponent. + int exponent = 0; + *result = DiyFp(significand, exponent); + *remaining_decimals = buffer.length() - read_digits; + } + } + + + static bool DoubleStrtod(Vector<const char> trimmed, + int exponent, + double* result) { +#if !defined(DOUBLE_CONVERSION_CORRECT_DOUBLE_OPERATIONS) + // On x86 the floating-point stack can be 64 or 80 bits wide. If it is + // 80 bits wide (as is the case on Linux) then double-rounding occurs and the + // result is not accurate. + // We know that Windows32 uses 64 bits and is therefore accurate. + // Note that the ARM simulator is compiled for 32bits. It therefore exhibits + // the same problem. + return false; +#endif + if (trimmed.length() <= kMaxExactDoubleIntegerDecimalDigits) { + int read_digits; + // The trimmed input fits into a double. + // If the 10^exponent (resp. 10^-exponent) fits into a double too then we + // can compute the result-double simply by multiplying (resp. dividing) the + // two numbers. + // This is possible because IEEE guarantees that floating-point operations + // return the best possible approximation. + if (exponent < 0 && -exponent < kExactPowersOfTenSize) { + // 10^-exponent fits into a double. + *result = static_cast<double>(ReadUint64(trimmed, &read_digits)); + ASSERT(read_digits == trimmed.length()); + *result /= exact_powers_of_ten[-exponent]; + return true; + } + if (0 <= exponent && exponent < kExactPowersOfTenSize) { + // 10^exponent fits into a double. + *result = static_cast<double>(ReadUint64(trimmed, &read_digits)); + ASSERT(read_digits == trimmed.length()); + *result *= exact_powers_of_ten[exponent]; + return true; + } + int remaining_digits = + kMaxExactDoubleIntegerDecimalDigits - trimmed.length(); + if ((0 <= exponent) && + (exponent - remaining_digits < kExactPowersOfTenSize)) { + // The trimmed string was short and we can multiply it with + // 10^remaining_digits. As a result the remaining exponent now fits + // into a double too. + *result = static_cast<double>(ReadUint64(trimmed, &read_digits)); + ASSERT(read_digits == trimmed.length()); + *result *= exact_powers_of_ten[remaining_digits]; + *result *= exact_powers_of_ten[exponent - remaining_digits]; + return true; + } + } + return false; + } + + + // Returns 10^exponent as an exact DiyFp. + // The given exponent must be in the range [1; kDecimalExponentDistance[. + static DiyFp AdjustmentPowerOfTen(int exponent) { + ASSERT(0 < exponent); + ASSERT(exponent < PowersOfTenCache::kDecimalExponentDistance); + // Simply hardcode the remaining powers for the given decimal exponent + // distance. + ASSERT(PowersOfTenCache::kDecimalExponentDistance == 8); + switch (exponent) { + case 1: return DiyFp(UINT64_2PART_C(0xa0000000, 00000000), -60); + case 2: return DiyFp(UINT64_2PART_C(0xc8000000, 00000000), -57); + case 3: return DiyFp(UINT64_2PART_C(0xfa000000, 00000000), -54); + case 4: return DiyFp(UINT64_2PART_C(0x9c400000, 00000000), -50); + case 5: return DiyFp(UINT64_2PART_C(0xc3500000, 00000000), -47); + case 6: return DiyFp(UINT64_2PART_C(0xf4240000, 00000000), -44); + case 7: return DiyFp(UINT64_2PART_C(0x98968000, 00000000), -40); + default: + UNREACHABLE(); + return DiyFp(0, 0); + } + } + + + // If the function returns true then the result is the correct double. + // Otherwise it is either the correct double or the double that is just below + // the correct double. + static bool DiyFpStrtod(Vector<const char> buffer, + int exponent, + double* result) { + DiyFp input; + int remaining_decimals; + ReadDiyFp(buffer, &input, &remaining_decimals); + // Since we may have dropped some digits the input is not accurate. + // If remaining_decimals is different than 0 than the error is at most + // .5 ulp (unit in the last place). + // We don't want to deal with fractions and therefore keep a common + // denominator. + const int kDenominatorLog = 3; + const int kDenominator = 1 << kDenominatorLog; + // Move the remaining decimals into the exponent. + exponent += remaining_decimals; + int error = (remaining_decimals == 0 ? 0 : kDenominator / 2); + + int old_e = input.e(); + input.Normalize(); + error <<= old_e - input.e(); + + ASSERT(exponent <= PowersOfTenCache::kMaxDecimalExponent); + if (exponent < PowersOfTenCache::kMinDecimalExponent) { + *result = 0.0; + return true; + } + DiyFp cached_power; + int cached_decimal_exponent; + PowersOfTenCache::GetCachedPowerForDecimalExponent(exponent, + &cached_power, + &cached_decimal_exponent); + + if (cached_decimal_exponent != exponent) { + int adjustment_exponent = exponent - cached_decimal_exponent; + DiyFp adjustment_power = AdjustmentPowerOfTen(adjustment_exponent); + input.Multiply(adjustment_power); + if (kMaxUint64DecimalDigits - buffer.length() >= adjustment_exponent) { + // The product of input with the adjustment power fits into a 64 bit + // integer. + ASSERT(DiyFp::kSignificandSize == 64); + } else { + // The adjustment power is exact. There is hence only an error of 0.5. + error += kDenominator / 2; + } + } + + input.Multiply(cached_power); + // The error introduced by a multiplication of a*b equals + // error_a + error_b + error_a*error_b/2^64 + 0.5 + // Substituting a with 'input' and b with 'cached_power' we have + // error_b = 0.5 (all cached powers have an error of less than 0.5 ulp), + // error_ab = 0 or 1 / kDenominator > error_a*error_b/ 2^64 + int error_b = kDenominator / 2; + int error_ab = (error == 0 ? 0 : 1); // We round up to 1. + int fixed_error = kDenominator / 2; + error += error_b + error_ab + fixed_error; + + old_e = input.e(); + input.Normalize(); + error <<= old_e - input.e(); + + // See if the double's significand changes if we add/subtract the error. + int order_of_magnitude = DiyFp::kSignificandSize + input.e(); + int effective_significand_size = + Double::SignificandSizeForOrderOfMagnitude(order_of_magnitude); + int precision_digits_count = + DiyFp::kSignificandSize - effective_significand_size; + if (precision_digits_count + kDenominatorLog >= DiyFp::kSignificandSize) { + // This can only happen for very small denormals. In this case the + // half-way multiplied by the denominator exceeds the range of an uint64. + // Simply shift everything to the right. + int shift_amount = (precision_digits_count + kDenominatorLog) - + DiyFp::kSignificandSize + 1; + input.set_f(input.f() >> shift_amount); + input.set_e(input.e() + shift_amount); + // We add 1 for the lost precision of error, and kDenominator for + // the lost precision of input.f(). + error = (error >> shift_amount) + 1 + kDenominator; + precision_digits_count -= shift_amount; + } + // We use uint64_ts now. This only works if the DiyFp uses uint64_ts too. + ASSERT(DiyFp::kSignificandSize == 64); + ASSERT(precision_digits_count < 64); + uint64_t one64 = 1; + uint64_t precision_bits_mask = (one64 << precision_digits_count) - 1; + uint64_t precision_bits = input.f() & precision_bits_mask; + uint64_t half_way = one64 << (precision_digits_count - 1); + precision_bits *= kDenominator; + half_way *= kDenominator; + DiyFp rounded_input(input.f() >> precision_digits_count, + input.e() + precision_digits_count); + if (precision_bits >= half_way + error) { + rounded_input.set_f(rounded_input.f() + 1); + } + // If the last_bits are too close to the half-way case than we are too + // inaccurate and round down. In this case we return false so that we can + // fall back to a more precise algorithm. + + *result = Double(rounded_input).value(); + if (half_way - error < precision_bits && precision_bits < half_way + error) { + // Too imprecise. The caller will have to fall back to a slower version. + // However the returned number is guaranteed to be either the correct + // double, or the next-lower double. + return false; + } else { + return true; + } + } + + + // Returns the correct double for the buffer*10^exponent. + // The variable guess should be a close guess that is either the correct double + // or its lower neighbor (the nearest double less than the correct one). + // Preconditions: + // buffer.length() + exponent <= kMaxDecimalPower + 1 + // buffer.length() + exponent > kMinDecimalPower + // buffer.length() <= kMaxDecimalSignificantDigits + static double BignumStrtod(Vector<const char> buffer, + int exponent, + double guess) { + if (guess == Double::Infinity()) { + return guess; + } + + DiyFp upper_boundary = Double(guess).UpperBoundary(); + + ASSERT(buffer.length() + exponent <= kMaxDecimalPower + 1); + ASSERT(buffer.length() + exponent > kMinDecimalPower); + ASSERT(buffer.length() <= kMaxSignificantDecimalDigits); + // Make sure that the Bignum will be able to hold all our numbers. + // Our Bignum implementation has a separate field for exponents. Shifts will + // consume at most one bigit (< 64 bits). + // ln(10) == 3.3219... + ASSERT(((kMaxDecimalPower + 1) * 333 / 100) < Bignum::kMaxSignificantBits); + Bignum input; + Bignum boundary; + input.AssignDecimalString(buffer); + boundary.AssignUInt64(upper_boundary.f()); + if (exponent >= 0) { + input.MultiplyByPowerOfTen(exponent); + } else { + boundary.MultiplyByPowerOfTen(-exponent); + } + if (upper_boundary.e() > 0) { + boundary.ShiftLeft(upper_boundary.e()); + } else { + input.ShiftLeft(-upper_boundary.e()); + } + int comparison = Bignum::Compare(input, boundary); + if (comparison < 0) { + return guess; + } else if (comparison > 0) { + return Double(guess).NextDouble(); + } else if ((Double(guess).Significand() & 1) == 0) { + // Round towards even. + return guess; + } else { + return Double(guess).NextDouble(); + } + } + + + double Strtod(Vector<const char> buffer, int exponent) { + Vector<const char> left_trimmed = TrimLeadingZeros(buffer); + Vector<const char> trimmed = TrimTrailingZeros(left_trimmed); + exponent += left_trimmed.length() - trimmed.length(); + if (trimmed.length() == 0) return 0.0; + if (trimmed.length() > kMaxSignificantDecimalDigits) { + char significant_buffer[kMaxSignificantDecimalDigits]; + int significant_exponent; + TrimToMaxSignificantDigits(trimmed, exponent, + significant_buffer, &significant_exponent); + return Strtod(Vector<const char>(significant_buffer, + kMaxSignificantDecimalDigits), + significant_exponent); + } + if (exponent + trimmed.length() - 1 >= kMaxDecimalPower) { + return Double::Infinity(); + } + if (exponent + trimmed.length() <= kMinDecimalPower) { + return 0.0; + } + + double guess; + if (DoubleStrtod(trimmed, exponent, &guess) || + DiyFpStrtod(trimmed, exponent, &guess)) { + return guess; + } + return BignumStrtod(trimmed, exponent, guess); + } + +} // namespace double_conversion + +} // namespace WTF diff --git a/Source/JavaScriptCore/wtf/dtoa/strtod.h b/Source/JavaScriptCore/wtf/dtoa/strtod.h new file mode 100644 index 000000000..8ed350ad8 --- /dev/null +++ b/Source/JavaScriptCore/wtf/dtoa/strtod.h @@ -0,0 +1,45 @@ +// Copyright 2010 the V8 project authors. 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 Google Inc. 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. + +#ifndef DOUBLE_CONVERSION_STRTOD_H_ +#define DOUBLE_CONVERSION_STRTOD_H_ + +#include "utils.h" + +namespace WTF { + +namespace double_conversion { + + // The buffer must only contain digits in the range [0-9]. It must not + // contain a dot or a sign. It must not start with '0', and must not be empty. + double Strtod(Vector<const char> buffer, int exponent); + +} // namespace double_conversion + +} // namespace WTF + +#endif // DOUBLE_CONVERSION_STRTOD_H_ diff --git a/Source/JavaScriptCore/wtf/dtoa/utils.h b/Source/JavaScriptCore/wtf/dtoa/utils.h new file mode 100644 index 000000000..d5cfe9c29 --- /dev/null +++ b/Source/JavaScriptCore/wtf/dtoa/utils.h @@ -0,0 +1,310 @@ +// Copyright 2010 the V8 project authors. 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 Google Inc. 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. + +#ifndef DOUBLE_CONVERSION_UTILS_H_ +#define DOUBLE_CONVERSION_UTILS_H_ + +#include "Assertions.h" +#include <stdlib.h> +#include <string.h> + +#define UNIMPLEMENTED ASSERT_NOT_REACHED +#define UNREACHABLE ASSERT_NOT_REACHED + +// Double operations detection based on target architecture. +// Linux uses a 80bit wide floating point stack on x86. This induces double +// rounding, which in turn leads to wrong results. +// An easy way to test if the floating-point operations are correct is to +// evaluate: 89255.0/1e22. If the floating-point stack is 64 bits wide then +// the result is equal to 89255e-22. +// The best way to test this, is to create a division-function and to compare +// the output of the division with the expected result. (Inlining must be +// disabled.) +// On Linux,x86 89255e-22 != Div_double(89255.0/1e22) +#if defined(_M_X64) || defined(__x86_64__) || \ +defined(__ARMEL__) || \ +defined(_MIPS_ARCH_MIPS32R2) +#define DOUBLE_CONVERSION_CORRECT_DOUBLE_OPERATIONS 1 +#elif CPU(MIPS) || CPU(PPC) || CPU(PPC64) || OS(WINCE) || CPU(SH4) || CPU(S390) || CPU(S390X) +#define DOUBLE_CONVERSION_CORRECT_DOUBLE_OPERATIONS 1 +#elif defined(_M_IX86) || defined(__i386__) +#if defined(_WIN32) +// Windows uses a 64bit wide floating point stack. +#define DOUBLE_CONVERSION_CORRECT_DOUBLE_OPERATIONS 1 +#else +#undef DOUBLE_CONVERSION_CORRECT_DOUBLE_OPERATIONS +#endif // _WIN32 +#else +#error Target architecture was not detected as supported by Double-Conversion. +#endif + + +#if defined(_WIN32) && !defined(__MINGW32__) + +typedef signed char int8_t; +typedef unsigned char uint8_t; +typedef short int16_t; // NOLINT +typedef unsigned short uint16_t; // NOLINT +typedef int int32_t; +typedef unsigned int uint32_t; +typedef __int64 int64_t; +typedef unsigned __int64 uint64_t; +// intptr_t and friends are defined in crtdefs.h through stdio.h. + +#else + +#include <stdint.h> + +#endif + +// The following macro works on both 32 and 64-bit platforms. +// Usage: instead of writing 0x1234567890123456 +// write UINT64_2PART_C(0x12345678,90123456); +#define UINT64_2PART_C(a, b) (((static_cast<uint64_t>(a) << 32) + 0x##b##u)) + + +// The expression ARRAY_SIZE(a) is a compile-time constant of type +// size_t which represents the number of elements of the given +// array. You should only use ARRAY_SIZE on statically allocated +// arrays. +#define ARRAY_SIZE(a) \ +((sizeof(a) / sizeof(*(a))) / \ +static_cast<size_t>(!(sizeof(a) % sizeof(*(a))))) + +// A macro to disallow the evil copy constructor and operator= functions +// This should be used in the private: declarations for a class +#define DISALLOW_COPY_AND_ASSIGN(TypeName) \ +TypeName(const TypeName&); \ +void operator=(const TypeName&) + +// A macro to disallow all the implicit constructors, namely the +// default constructor, copy constructor and operator= functions. +// +// This should be used in the private: declarations for a class +// that wants to prevent anyone from instantiating it. This is +// especially useful for classes containing only static methods. +#define DISALLOW_IMPLICIT_CONSTRUCTORS(TypeName) \ +TypeName(); \ +DISALLOW_COPY_AND_ASSIGN(TypeName) + +namespace WTF { + +namespace double_conversion { + + static const int kCharSize = sizeof(char); + + // Returns the maximum of the two parameters. + template <typename T> + static T Max(T a, T b) { + return a < b ? b : a; + } + + + // Returns the minimum of the two parameters. + template <typename T> + static T Min(T a, T b) { + return a < b ? a : b; + } + + + inline int StrLength(const char* string) { + size_t length = strlen(string); + ASSERT(length == static_cast<size_t>(static_cast<int>(length))); + return static_cast<int>(length); + } + + // This is a simplified version of V8's Vector class. + template <typename T> + class Vector { + public: + Vector() : start_(NULL), length_(0) {} + Vector(T* data, int length) : start_(data), length_(length) { + ASSERT(length == 0 || (length > 0 && data != NULL)); + } + + // Returns a vector using the same backing storage as this one, + // spanning from and including 'from', to but not including 'to'. + Vector<T> SubVector(int from, int to) { + ASSERT(to <= length_); + ASSERT(from < to); + ASSERT(0 <= from); + return Vector<T>(start() + from, to - from); + } + + // Returns the length of the vector. + int length() const { return length_; } + + // Returns whether or not the vector is empty. + bool is_empty() const { return length_ == 0; } + + // Returns the pointer to the start of the data in the vector. + T* start() const { return start_; } + + // Access individual vector elements - checks bounds in debug mode. + T& operator[](int index) const { + ASSERT(0 <= index && index < length_); + return start_[index]; + } + + T& first() { return start_[0]; } + + T& last() { return start_[length_ - 1]; } + + private: + T* start_; + int length_; + }; + + + // Helper class for building result strings in a character buffer. The + // purpose of the class is to use safe operations that checks the + // buffer bounds on all operations in debug mode. + class StringBuilder { + public: + StringBuilder(char* buffer, int size) + : buffer_(buffer, size), position_(0) { } + + ~StringBuilder() { if (!is_finalized()) Finalize(); } + + int size() const { return buffer_.length(); } + + // Get the current position in the builder. + int position() const { + ASSERT(!is_finalized()); + return position_; + } + + // Set the current position in the builder. + void SetPosition(int position) + { + ASSERT(!is_finalized()); + ASSERT(position < size()); + position_ = position; + } + + // Reset the position. + void Reset() { position_ = 0; } + + // Add a single character to the builder. It is not allowed to add + // 0-characters; use the Finalize() method to terminate the string + // instead. + void AddCharacter(char c) { + ASSERT(c != '\0'); + ASSERT(!is_finalized() && position_ < buffer_.length()); + buffer_[position_++] = c; + } + + // Add an entire string to the builder. Uses strlen() internally to + // compute the length of the input string. + void AddString(const char* s) { + AddSubstring(s, StrLength(s)); + } + + // Add the first 'n' characters of the given string 's' to the + // builder. The input string must have enough characters. + void AddSubstring(const char* s, int n) { + ASSERT(!is_finalized() && position_ + n < buffer_.length()); + ASSERT(static_cast<size_t>(n) <= strlen(s)); + memcpy(&buffer_[position_], s, n * kCharSize); + position_ += n; + } + + + // Add character padding to the builder. If count is non-positive, + // nothing is added to the builder. + void AddPadding(char c, int count) { + for (int i = 0; i < count; i++) { + AddCharacter(c); + } + } + + // Finalize the string by 0-terminating it and returning the buffer. + char* Finalize() { + ASSERT(!is_finalized() && position_ < buffer_.length()); + buffer_[position_] = '\0'; + // Make sure nobody managed to add a 0-character to the + // buffer while building the string. + ASSERT(strlen(buffer_.start()) == static_cast<size_t>(position_)); + position_ = -1; + ASSERT(is_finalized()); + return buffer_.start(); + } + + private: + Vector<char> buffer_; + int position_; + + bool is_finalized() const { return position_ < 0; } + + DISALLOW_IMPLICIT_CONSTRUCTORS(StringBuilder); + }; + + // The type-based aliasing rule allows the compiler to assume that pointers of + // different types (for some definition of different) never alias each other. + // Thus the following code does not work: + // + // float f = foo(); + // int fbits = *(int*)(&f); + // + // The compiler 'knows' that the int pointer can't refer to f since the types + // don't match, so the compiler may cache f in a register, leaving random data + // in fbits. Using C++ style casts makes no difference, however a pointer to + // char data is assumed to alias any other pointer. This is the 'memcpy + // exception'. + // + // Bit_cast uses the memcpy exception to move the bits from a variable of one + // type of a variable of another type. Of course the end result is likely to + // be implementation dependent. Most compilers (gcc-4.2 and MSVC 2005) + // will completely optimize BitCast away. + // + // There is an additional use for BitCast. + // Recent gccs will warn when they see casts that may result in breakage due to + // the type-based aliasing rule. If you have checked that there is no breakage + // you can use BitCast to cast one pointer type to another. This confuses gcc + // enough that it can no longer see that you have cast one pointer type to + // another thus avoiding the warning. + template <class Dest, class Source> + inline Dest BitCast(const Source& source) { + // Compile time assertion: sizeof(Dest) == sizeof(Source) + // A compile error here means your Dest and Source have different sizes. + typedef char VerifySizesAreEqual[sizeof(Dest) == sizeof(Source) ? 1 : -1]; + + Dest dest; + memcpy(&dest, &source, sizeof(dest)); + return dest; + } + + template <class Dest, class Source> + inline Dest BitCast(Source* source) { + return BitCast<Dest>(reinterpret_cast<uintptr_t>(source)); + } + +} // namespace double_conversion + +} // namespace WTF + +#endif // DOUBLE_CONVERSION_UTILS_H_ diff --git a/Source/JavaScriptCore/wtf/efl/MainThreadEfl.cpp b/Source/JavaScriptCore/wtf/efl/MainThreadEfl.cpp new file mode 100644 index 000000000..53adcb2b1 --- /dev/null +++ b/Source/JavaScriptCore/wtf/efl/MainThreadEfl.cpp @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2007, 2008 Apple Inc. All rights reserved. + * Copyright (C) 2007 Justin Haygood (jhaygood@reaktix.com) + * Copyright (C) 2008 Diego Gonzalez + * Copyright (C) 2008 Kenneth Rohde Christiansen + * Copyright (C) 2009-2010 ProFUSION embedded systems + * Copyright (C) 2009-2010 Samsung Electronics + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of Apple Inc. ("Apple") 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 APPLE AND ITS 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 APPLE OR ITS 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 "config.h" +#include "MainThread.h" + +#include <Ecore.h> +#include <wtf/OwnPtr.h> +#include <wtf/PassOwnPtr.h> +#include <wtf/StdLibExtras.h> + +namespace WTF { + +static OwnPtr<Ecore_Pipe>& pipeObject() +{ + DEFINE_STATIC_LOCAL(OwnPtr<Ecore_Pipe>, pipeObject, ()); + return pipeObject; +} + +static void monitorDispatchFunctions(void*, void*, unsigned int) +{ + dispatchFunctionsFromMainThread(); +} + +void initializeMainThreadPlatform() +{ + pipeObject() = adoptPtr(ecore_pipe_add(monitorDispatchFunctions, 0)); +} + +void scheduleDispatchFunctionsOnMainThread() +{ + ecore_pipe_write(pipeObject().get(), "", 0); +} + +} diff --git a/Source/JavaScriptCore/wtf/efl/OwnPtrEfl.cpp b/Source/JavaScriptCore/wtf/efl/OwnPtrEfl.cpp new file mode 100644 index 000000000..7ec517029 --- /dev/null +++ b/Source/JavaScriptCore/wtf/efl/OwnPtrEfl.cpp @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2011 ProFUSION embedded systems + * Copyright (C) 2011 Samsung Electronics + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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 APPLE COMPUTER, INC. ``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 APPLE COMPUTER, INC. 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 "config.h" +#include "OwnPtr.h" + +#include <Ecore.h> +#include <Ecore_Evas.h> +#include <Evas.h> + +namespace WTF { + +void deleteOwnedPtr(Ecore_Evas* ptr) +{ + if (ptr) + ecore_evas_free(ptr); +} + +void deleteOwnedPtr(Evas_Object* ptr) +{ + evas_object_del(ptr); +} + +void deleteOwnedPtr(Ecore_Pipe* ptr) +{ + if (ptr) + ecore_pipe_del(ptr); +} + +} diff --git a/Source/JavaScriptCore/wtf/gobject/GOwnPtr.cpp b/Source/JavaScriptCore/wtf/gobject/GOwnPtr.cpp new file mode 100644 index 000000000..50c7b9ffc --- /dev/null +++ b/Source/JavaScriptCore/wtf/gobject/GOwnPtr.cpp @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2008 Collabora Ltd. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "config.h" +#include "GOwnPtr.h" + +#if ENABLE(GLIB_SUPPORT) + +#include <gio/gio.h> +#include <glib.h> + +namespace WTF { + +template <> void freeOwnedGPtr<GError>(GError* ptr) +{ + if (ptr) + g_error_free(ptr); +} + +template <> void freeOwnedGPtr<GList>(GList* ptr) +{ + g_list_free(ptr); +} + +template <> void freeOwnedGPtr<GPatternSpec>(GPatternSpec* ptr) +{ + if (ptr) + g_pattern_spec_free(ptr); +} + +template <> void freeOwnedGPtr<GDir>(GDir* ptr) +{ + if (ptr) + g_dir_close(ptr); +} + +} // namespace WTF + +#endif // ENABLE(GLIB_SUPPORT) diff --git a/Source/JavaScriptCore/wtf/gobject/GOwnPtr.h b/Source/JavaScriptCore/wtf/gobject/GOwnPtr.h new file mode 100644 index 000000000..9ff85c5a4 --- /dev/null +++ b/Source/JavaScriptCore/wtf/gobject/GOwnPtr.h @@ -0,0 +1,141 @@ +/* + * Copyright (C) 2006, 2007 Apple Inc. All rights reserved. + * Copyright (C) 2008 Collabora Ltd. + * + * This library 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 library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#ifndef GOwnPtr_h +#define GOwnPtr_h + +#if ENABLE(GLIB_SUPPORT) + +#include <algorithm> +#include <wtf/Assertions.h> +#include <wtf/Noncopyable.h> + +extern "C" void g_free(void*); + +namespace WTF { + +template <typename T> inline void freeOwnedGPtr(T* ptr); +template<> void freeOwnedGPtr<GError>(GError*); +template<> void freeOwnedGPtr<GList>(GList*); +template<> void freeOwnedGPtr<GPatternSpec>(GPatternSpec*); +template<> void freeOwnedGPtr<GDir>(GDir*); + +template <typename T> class GOwnPtr { + WTF_MAKE_NONCOPYABLE(GOwnPtr); +public: + explicit GOwnPtr(T* ptr = 0) : m_ptr(ptr) { } + ~GOwnPtr() { freeOwnedGPtr(m_ptr); } + + T* get() const { return m_ptr; } + T* release() + { + T* ptr = m_ptr; + m_ptr = 0; + return ptr; + } + + T*& outPtr() + { + ASSERT(!m_ptr); + return m_ptr; + } + + void set(T* ptr) + { + ASSERT(!ptr || m_ptr != ptr); + freeOwnedGPtr(m_ptr); + m_ptr = ptr; + } + + void clear() + { + T* ptr = m_ptr; + m_ptr = 0; + freeOwnedGPtr(ptr); + } + + T& operator*() const + { + ASSERT(m_ptr); + return *m_ptr; + } + + T* operator->() const + { + ASSERT(m_ptr); + return m_ptr; + } + + bool operator!() const { return !m_ptr; } + + // This conversion operator allows implicit conversion to bool but not to other integer types. + typedef T* GOwnPtr::*UnspecifiedBoolType; + operator UnspecifiedBoolType() const { return m_ptr ? &GOwnPtr::m_ptr : 0; } + + void swap(GOwnPtr& o) { std::swap(m_ptr, o.m_ptr); } + +private: + T* m_ptr; +}; + +template <typename T> inline void swap(GOwnPtr<T>& a, GOwnPtr<T>& b) +{ + a.swap(b); +} + +template <typename T, typename U> inline bool operator==(const GOwnPtr<T>& a, U* b) +{ + return a.get() == b; +} + +template <typename T, typename U> inline bool operator==(T* a, const GOwnPtr<U>& b) +{ + return a == b.get(); +} + +template <typename T, typename U> inline bool operator!=(const GOwnPtr<T>& a, U* b) +{ + return a.get() != b; +} + +template <typename T, typename U> inline bool operator!=(T* a, const GOwnPtr<U>& b) +{ + return a != b.get(); +} + +template <typename T> inline typename GOwnPtr<T>::PtrType getPtr(const GOwnPtr<T>& p) +{ + return p.get(); +} + +template <typename T> inline void freeOwnedGPtr(T* ptr) +{ + g_free(ptr); +} + +} // namespace WTF + +using WTF::GOwnPtr; + +#endif // ENABLE(GLIB_SUPPORT) + +#endif // GOwnPtr_h + diff --git a/Source/JavaScriptCore/wtf/gobject/GRefPtr.cpp b/Source/JavaScriptCore/wtf/gobject/GRefPtr.cpp new file mode 100644 index 000000000..1cd22c532 --- /dev/null +++ b/Source/JavaScriptCore/wtf/gobject/GRefPtr.cpp @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2009 Martin Robinson + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "config.h" +#include "GRefPtr.h" + +#if ENABLE(GLIB_SUPPORT) + +#include <glib.h> + +namespace WTF { + +template <> GHashTable* refGPtr(GHashTable* ptr) +{ + if (ptr) + g_hash_table_ref(ptr); + return ptr; +} + +template <> void derefGPtr(GHashTable* ptr) +{ + g_hash_table_unref(ptr); +} + +#if GLIB_CHECK_VERSION(2, 24, 0) +template <> GVariant* refGPtr(GVariant* ptr) +{ + if (ptr) + g_variant_ref(ptr); + return ptr; +} + +template <> void derefGPtr(GVariant* ptr) +{ + g_variant_unref(ptr); +} + +#else + +// We do this so that we can avoid including the glib.h header in GRefPtr.h. +typedef struct _GVariant { + bool fake; +} GVariant; + +template <> GVariant* refGPtr(GVariant* ptr) +{ + return ptr; +} + +template <> void derefGPtr(GVariant* ptr) +{ +} + +#endif + +template <> GSource* refGPtr(GSource* ptr) +{ + if (ptr) + g_source_ref(ptr); + return ptr; +} + +template <> void derefGPtr(GSource* ptr) +{ + if (ptr) + g_source_unref(ptr); +} + +} // namespace WTF + +#endif // ENABLE(GLIB_SUPPORT) diff --git a/Source/JavaScriptCore/wtf/gobject/GRefPtr.h b/Source/JavaScriptCore/wtf/gobject/GRefPtr.h new file mode 100644 index 000000000..10ebf0753 --- /dev/null +++ b/Source/JavaScriptCore/wtf/gobject/GRefPtr.h @@ -0,0 +1,229 @@ +/* + * Copyright (C) 2005, 2006, 2007, 2008 Apple Inc. All rights reserved. + * Copyright (C) 2008 Collabora Ltd. + * Copyright (C) 2009 Martin Robinson + * + * This library 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 library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#ifndef WTF_GRefPtr_h +#define WTF_GRefPtr_h + +#if ENABLE(GLIB_SUPPORT) + +#include "AlwaysInline.h" +#include "GRefPtr.h" +#include "RefPtr.h" +#include <algorithm> + +extern "C" void g_object_unref(gpointer); +extern "C" gpointer g_object_ref_sink(gpointer); + +namespace WTF { + +enum GRefPtrAdoptType { GRefPtrAdopt }; +template <typename T> inline T* refGPtr(T*); +template <typename T> inline void derefGPtr(T*); +template <typename T> class GRefPtr; +template <typename T> GRefPtr<T> adoptGRef(T*); + +template <typename T> class GRefPtr { +public: + GRefPtr() : m_ptr(0) { } + + GRefPtr(T* ptr) + : m_ptr(ptr) + { + if (ptr) + refGPtr(ptr); + } + + GRefPtr(const GRefPtr& o) + : m_ptr(o.m_ptr) + { + if (T* ptr = m_ptr) + refGPtr(ptr); + } + + template <typename U> GRefPtr(const GRefPtr<U>& o) + : m_ptr(o.get()) + { + if (T* ptr = m_ptr) + refGPtr(ptr); + } + + ~GRefPtr() + { + if (T* ptr = m_ptr) + derefGPtr(ptr); + } + + void clear() + { + T* ptr = m_ptr; + m_ptr = 0; + if (ptr) + derefGPtr(ptr); + } + + T* leakRef() WARN_UNUSED_RETURN + { + T* ptr = m_ptr; + m_ptr = 0; + return ptr; + } + + // Hash table deleted values, which are only constructed and never copied or destroyed. + GRefPtr(HashTableDeletedValueType) : m_ptr(hashTableDeletedValue()) { } + bool isHashTableDeletedValue() const { return m_ptr == hashTableDeletedValue(); } + + T* get() const { return m_ptr; } + T& operator*() const { return *m_ptr; } + ALWAYS_INLINE T* operator->() const { return m_ptr; } + + bool operator!() const { return !m_ptr; } + + // This conversion operator allows implicit conversion to bool but not to other integer types. + typedef T* GRefPtr::*UnspecifiedBoolType; + operator UnspecifiedBoolType() const { return m_ptr ? &GRefPtr::m_ptr : 0; } + + GRefPtr& operator=(const GRefPtr&); + GRefPtr& operator=(T*); + template <typename U> GRefPtr& operator=(const GRefPtr<U>&); + + void swap(GRefPtr&); + friend GRefPtr adoptGRef<T>(T*); + +private: + static T* hashTableDeletedValue() { return reinterpret_cast<T*>(-1); } + // Adopting constructor. + GRefPtr(T* ptr, GRefPtrAdoptType) : m_ptr(ptr) {} + + T* m_ptr; +}; + +template <typename T> inline GRefPtr<T>& GRefPtr<T>::operator=(const GRefPtr<T>& o) +{ + T* optr = o.get(); + if (optr) + refGPtr(optr); + T* ptr = m_ptr; + m_ptr = optr; + if (ptr) + derefGPtr(ptr); + return *this; +} + +template <typename T> inline GRefPtr<T>& GRefPtr<T>::operator=(T* optr) +{ + T* ptr = m_ptr; + if (optr) + refGPtr(optr); + m_ptr = optr; + if (ptr) + derefGPtr(ptr); + return *this; +} + +template <class T> inline void GRefPtr<T>::swap(GRefPtr<T>& o) +{ + std::swap(m_ptr, o.m_ptr); +} + +template <class T> inline void swap(GRefPtr<T>& a, GRefPtr<T>& b) +{ + a.swap(b); +} + +template <typename T, typename U> inline bool operator==(const GRefPtr<T>& a, const GRefPtr<U>& b) +{ + return a.get() == b.get(); +} + +template <typename T, typename U> inline bool operator==(const GRefPtr<T>& a, U* b) +{ + return a.get() == b; +} + +template <typename T, typename U> inline bool operator==(T* a, const GRefPtr<U>& b) +{ + return a == b.get(); +} + +template <typename T, typename U> inline bool operator!=(const GRefPtr<T>& a, const GRefPtr<U>& b) +{ + return a.get() != b.get(); +} + +template <typename T, typename U> inline bool operator!=(const GRefPtr<T>& a, U* b) +{ + return a.get() != b; +} + +template <typename T, typename U> inline bool operator!=(T* a, const GRefPtr<U>& b) +{ + return a != b.get(); +} + +template <typename T, typename U> inline GRefPtr<T> static_pointer_cast(const GRefPtr<U>& p) +{ + return GRefPtr<T>(static_cast<T*>(p.get())); +} + +template <typename T, typename U> inline GRefPtr<T> const_pointer_cast(const GRefPtr<U>& p) +{ + return GRefPtr<T>(const_cast<T*>(p.get())); +} + +template <typename T> inline T* getPtr(const GRefPtr<T>& p) +{ + return p.get(); +} + +template <typename T> GRefPtr<T> adoptGRef(T* p) +{ + return GRefPtr<T>(p, GRefPtrAdopt); +} + +template <> GHashTable* refGPtr(GHashTable* ptr); +template <> void derefGPtr(GHashTable* ptr); +template <> GVariant* refGPtr(GVariant* ptr); +template <> void derefGPtr(GVariant* ptr); +template <> GSource* refGPtr(GSource* ptr); +template <> void derefGPtr(GSource* ptr); + +template <typename T> inline T* refGPtr(T* ptr) +{ + if (ptr) + g_object_ref_sink(ptr); + return ptr; +} + +template <typename T> inline void derefGPtr(T* ptr) +{ + if (ptr) + g_object_unref(ptr); +} + +} // namespace WTF + +using WTF::GRefPtr; +using WTF::adoptGRef; + +#endif // ENABLE(GLIB_SUPPORT) + +#endif // WTF_GRefPtr_h diff --git a/Source/JavaScriptCore/wtf/gobject/GTypedefs.h b/Source/JavaScriptCore/wtf/gobject/GTypedefs.h new file mode 100644 index 000000000..66cedd6d2 --- /dev/null +++ b/Source/JavaScriptCore/wtf/gobject/GTypedefs.h @@ -0,0 +1,95 @@ +/* + * Copyright (C) 2010 Igalia, S.L. + * + * This library 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 library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef GtkTypedefs_h +#define GtkTypedefs_h + +/* Vanilla C code does not seem to be able to handle forward-declaration typedefs. */ +#ifdef __cplusplus + +typedef char gchar; +typedef double gdouble; +typedef float gfloat; +typedef int gint; +typedef gint gboolean; +typedef long glong; +typedef short gshort; +typedef unsigned char guchar; +typedef unsigned int guint; +typedef unsigned long gulong; +typedef unsigned short gushort; +typedef void* gpointer; + +typedef struct _GAsyncResult GAsyncResult; +typedef struct _GCancellable GCancellable; +typedef struct _GCharsetConverter GCharsetConverter; +typedef struct _GDir GDir; +typedef struct _GdkAtom* GdkAtom; +typedef struct _GdkCursor GdkCursor; +typedef struct _GdkDragContext GdkDragContext; +typedef struct _GdkEventConfigure GdkEventConfigure; +typedef struct _GdkEventExpose GdkEventExpose; +typedef struct _GdkPixbuf GdkPixbuf; +typedef struct _GError GError; +typedef struct _GFile GFile; +typedef struct _GHashTable GHashTable; +typedef struct _GInputStream GInputStream; +typedef struct _GList GList; +typedef struct _GPatternSpec GPatternSpec; +typedef struct _GPollableOutputStream GPollableOutputStream; +typedef struct _GSocketClient GSocketClient; +typedef struct _GSocketConnection GSocketConnection; +typedef struct _GSource GSource; +typedef struct _GVariant GVariant; +typedef union _GdkEvent GdkEvent; + +#if USE(CAIRO) +typedef struct _cairo_surface cairo_surface_t; +typedef struct _cairo_rectangle_int cairo_rectangle_int_t; +#endif + +#if PLATFORM(GTK) +typedef struct _GtkAction GtkAction; +typedef struct _GtkAdjustment GtkAdjustment; +typedef struct _GtkBorder GtkBorder; +typedef struct _GtkClipboard GtkClipboard; +typedef struct _GtkContainer GtkContainer; +typedef struct _GtkIconInfo GtkIconInfo; +typedef struct _GtkMenu GtkMenu; +typedef struct _GtkMenuItem GtkMenuItem; +typedef struct _GtkObject GtkObject; +typedef struct _GtkSelectionData GtkSelectionData; +typedef struct _GtkStyle GtkStyle; +typedef struct _GtkTargetList GtkTargetList; +typedef struct _GtkThemeParts GtkThemeParts; +typedef struct _GtkWidget GtkWidget; +typedef struct _GtkWindow GtkWindow; + +#ifdef GTK_API_VERSION_2 +typedef struct _GdkRectangle GdkRectangle; +typedef struct _GdkDrawable GdkWindow; +#else +typedef struct _GdkWindow GdkWindow; +typedef struct _GtkStyleContext GtkStyleContext; +#endif + +#endif + +#endif +#endif /* GtkTypedefs_h */ diff --git a/Source/JavaScriptCore/wtf/gobject/GlibUtilities.cpp b/Source/JavaScriptCore/wtf/gobject/GlibUtilities.cpp new file mode 100644 index 000000000..6fcd806eb --- /dev/null +++ b/Source/JavaScriptCore/wtf/gobject/GlibUtilities.cpp @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2010 Igalia, S.L. + * + * This library 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 library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "config.h" +#include "GlibUtilities.h" + +#include <limits.h> +#include <unistd.h> + +#if OS(LINUX) +CString getCurrentExecutablePath() +{ + static char readLinkBuffer[PATH_MAX]; + ssize_t result = readlink("/proc/self/exe", readLinkBuffer, PATH_MAX); + if (result == -1) + return CString(); + return CString(readLinkBuffer, result); +} +#elif OS(UNIX) +CString getCurrentExecutablePath() +{ + static char readLinkBuffer[PATH_MAX]; + ssize_t result = readlink("/proc/curproc/file", readLinkBuffer, PATH_MAX); + if (result == -1) + return CString(); + return CString(readLinkBuffer, result); +} +#endif diff --git a/Source/JavaScriptCore/wtf/gobject/GlibUtilities.h b/Source/JavaScriptCore/wtf/gobject/GlibUtilities.h new file mode 100644 index 000000000..5577bf58e --- /dev/null +++ b/Source/JavaScriptCore/wtf/gobject/GlibUtilities.h @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2010 Igalia, S.L. + * + * This library 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 library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef GlibUtilities_h +#define GlibUtilities_h + +#include "Assertions.h" +#include "CString.h" + +CString getCurrentExecutablePath(); + +#endif diff --git a/Source/JavaScriptCore/wtf/gtk/MainThreadGtk.cpp b/Source/JavaScriptCore/wtf/gtk/MainThreadGtk.cpp new file mode 100644 index 000000000..7624247b6 --- /dev/null +++ b/Source/JavaScriptCore/wtf/gtk/MainThreadGtk.cpp @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2007, 2008 Apple Inc. All rights reserved. + * Copyright (C) 2007 Justin Haygood (jhaygood@reaktix.com) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of Apple Inc. ("Apple") 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 APPLE AND ITS 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 APPLE OR ITS 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 "config.h" +#include "MainThread.h" + +#include <glib.h> + +namespace WTF { + +void initializeMainThreadPlatform() +{ +} + +static gboolean timeoutFired(gpointer) +{ + dispatchFunctionsFromMainThread(); + return FALSE; +} + +void scheduleDispatchFunctionsOnMainThread() +{ + g_timeout_add(0, timeoutFired, 0); +} + +} // namespace WTF diff --git a/Source/JavaScriptCore/wtf/mac/MainThreadMac.mm b/Source/JavaScriptCore/wtf/mac/MainThreadMac.mm new file mode 100644 index 000000000..fbc625032 --- /dev/null +++ b/Source/JavaScriptCore/wtf/mac/MainThreadMac.mm @@ -0,0 +1,175 @@ +/* + * Copyright (C) 2007, 2008 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of Apple Computer, Inc. ("Apple") 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 APPLE AND ITS 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 APPLE OR ITS 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. + */ + +#import "config.h" +#import "MainThread.h" + +#import <CoreFoundation/CoreFoundation.h> +#import <Foundation/NSThread.h> +#import <stdio.h> +#import <wtf/Assertions.h> +#import <wtf/HashSet.h> +#import <wtf/Threading.h> +#import <wtf/ThreadSpecific.h> + +@interface JSWTFMainThreadCaller : NSObject { +} +- (void)call; +@end + +@implementation JSWTFMainThreadCaller + +- (void)call +{ + WTF::dispatchFunctionsFromMainThread(); +} + +@end // implementation JSWTFMainThreadCaller + +namespace WTF { + +static JSWTFMainThreadCaller* staticMainThreadCaller; +static bool isTimerPosted; // This is only accessed on the 'main' thread. +static bool mainThreadEstablishedAsPthreadMain; +static pthread_t mainThreadPthread; +static NSThread* mainThreadNSThread; + +#if ENABLE(PARALLEL_GC) +static ThreadSpecific<bool>* isGCThread; + +static void initializeGCThreads() +{ + isGCThread = new ThreadSpecific<bool>(); +} +#else +static void initializeGCThreads() { } +#endif + +void initializeMainThreadPlatform() +{ + ASSERT(!staticMainThreadCaller); + staticMainThreadCaller = [[JSWTFMainThreadCaller alloc] init]; + + mainThreadEstablishedAsPthreadMain = false; + mainThreadPthread = pthread_self(); + mainThreadNSThread = [[NSThread currentThread] retain]; + + initializeGCThreads(); +} + +void initializeMainThreadToProcessMainThreadPlatform() +{ + if (!pthread_main_np()) + NSLog(@"WebKit Threading Violation - initial use of WebKit from a secondary thread."); + + ASSERT(!staticMainThreadCaller); + staticMainThreadCaller = [[JSWTFMainThreadCaller alloc] init]; + + mainThreadEstablishedAsPthreadMain = true; + mainThreadPthread = 0; + mainThreadNSThread = nil; + + initializeGCThreads(); +} + +static void timerFired(CFRunLoopTimerRef timer, void*) +{ + CFRelease(timer); + isTimerPosted = false; + WTF::dispatchFunctionsFromMainThread(); +} + +static void postTimer() +{ + ASSERT(isMainThread()); + + if (isTimerPosted) + return; + + isTimerPosted = true; + CFRunLoopAddTimer(CFRunLoopGetCurrent(), CFRunLoopTimerCreate(0, 0, 0, 0, 0, timerFired, 0), kCFRunLoopCommonModes); +} + +void scheduleDispatchFunctionsOnMainThread() +{ + ASSERT(staticMainThreadCaller); + + if (isMainThread()) { + postTimer(); + return; + } + + if (mainThreadEstablishedAsPthreadMain) { + ASSERT(!mainThreadNSThread); + [staticMainThreadCaller performSelectorOnMainThread:@selector(call) withObject:nil waitUntilDone:NO]; + return; + } + + ASSERT(mainThreadNSThread); + [staticMainThreadCaller performSelector:@selector(call) onThread:mainThreadNSThread withObject:nil waitUntilDone:NO]; +} + +bool isMainThread() +{ + if (mainThreadEstablishedAsPthreadMain) { + ASSERT(!mainThreadPthread); + return pthread_main_np(); + } + + ASSERT(mainThreadPthread); + return pthread_equal(pthread_self(), mainThreadPthread); +} + +#if ENABLE(PARALLEL_GC) +void registerGCThread() +{ + if (!isGCThread) { + // This happens if we're running in a process that doesn't care about + // MainThread. + return; + } + + **isGCThread = true; +} + +bool isMainThreadOrGCThread() +{ + if (isGCThread->isSet() && **isGCThread) + return true; + + return isMainThread(); +} +#else +// This is necessary because JavaScriptCore.exp doesn't support preprocessor macros. +bool isMainThreadOrGCThread() +{ + return isMainThread(); +} +#endif + +} // namespace WTF diff --git a/Source/JavaScriptCore/wtf/qt/MainThreadQt.cpp b/Source/JavaScriptCore/wtf/qt/MainThreadQt.cpp new file mode 100644 index 000000000..606a7190e --- /dev/null +++ b/Source/JavaScriptCore/wtf/qt/MainThreadQt.cpp @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2007 Staikos Computing Services Inc. + * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies) + * Copyright (C) 2008 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of Apple Computer, Inc. ("Apple") 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 APPLE AND ITS 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 APPLE OR ITS 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 "config.h" +#include "MainThread.h" + +#include <QCoreApplication> +#include <QEvent> +#include <QObject> +#include <QThread> + +namespace WTF { + +static int s_mainThreadInvokerEventType; + +class MainThreadInvoker : public QObject { + Q_OBJECT +public: + MainThreadInvoker(); + virtual bool event(QEvent*); +}; + +MainThreadInvoker::MainThreadInvoker() +{ + s_mainThreadInvokerEventType = QEvent::registerEventType(); +} + +bool MainThreadInvoker::event(QEvent* e) +{ + if (e->type() != s_mainThreadInvokerEventType) + return QObject::event(e); + + dispatchFunctionsFromMainThread(); + return true; +} + +Q_GLOBAL_STATIC(MainThreadInvoker, webkit_main_thread_invoker) + +void initializeMainThreadPlatform() +{ + webkit_main_thread_invoker(); +} + +void scheduleDispatchFunctionsOnMainThread() +{ + QCoreApplication::postEvent(webkit_main_thread_invoker(), new QEvent(static_cast<QEvent::Type>(s_mainThreadInvokerEventType))); +} + +} // namespace WTF + +#include "MainThreadQt.moc" diff --git a/Source/JavaScriptCore/wtf/qt/StringQt.cpp b/Source/JavaScriptCore/wtf/qt/StringQt.cpp new file mode 100644 index 000000000..16dd439e3 --- /dev/null +++ b/Source/JavaScriptCore/wtf/qt/StringQt.cpp @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2006 Nikolas Zimmermann <zimmermann@kde.org> + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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 APPLE COMPUTER, INC. ``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 APPLE COMPUTER, INC. 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 "config.h" + +#include <wtf/StdLibExtras.h> +#include <wtf/text/WTFString.h> + +#include <QString> + +namespace WTF { + +// String conversions +String::String(const QString& qstr) +{ + if (qstr.isNull()) + return; + m_impl = StringImpl::create(reinterpret_cast_ptr<const UChar*>(qstr.constData()), qstr.length()); +} + +String::String(const QStringRef& ref) +{ + if (!ref.string()) + return; + m_impl = StringImpl::create(reinterpret_cast_ptr<const UChar*>(ref.unicode()), ref.length()); +} + +String::operator QString() const +{ + return QString(reinterpret_cast<const QChar*>(characters()), length()); +} + +QDataStream& operator<<(QDataStream& stream, const String& str) +{ + // could be faster + stream << QString(str); + return stream; +} + +QDataStream& operator>>(QDataStream& stream, String& str) +{ + // mabe not the fastest way, but really easy + QString tmp; + stream >> tmp; + str = tmp; + return stream; +} + +} + +// vim: ts=4 sw=4 et diff --git a/Source/JavaScriptCore/wtf/qt/UtilsQt.h b/Source/JavaScriptCore/wtf/qt/UtilsQt.h new file mode 100644 index 000000000..74067a8ee --- /dev/null +++ b/Source/JavaScriptCore/wtf/qt/UtilsQt.h @@ -0,0 +1,37 @@ +/* + Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies) + + This library 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 library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef WTF_UtilsQt_h +#define WTF_UtilsQt_h + +#include <QString> +#if QT_VERSION < QT_VERSION_CHECK(5, 0, 0) +#include <QTextDocument> +#endif + +inline QString escapeHtml(const QString& string) +{ +#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) + return string.toHtmlEscaped(); +#else + return Qt::escape(string); +#endif +} + +#endif // WTF_UtilsQt_h diff --git a/Source/JavaScriptCore/wtf/qt/compat/QGuiApplication b/Source/JavaScriptCore/wtf/qt/compat/QGuiApplication new file mode 100644 index 000000000..0337e2526 --- /dev/null +++ b/Source/JavaScriptCore/wtf/qt/compat/QGuiApplication @@ -0,0 +1 @@ +#include "qguiapplication.h" diff --git a/Source/JavaScriptCore/wtf/qt/compat/qguiapplication.h b/Source/JavaScriptCore/wtf/qt/compat/qguiapplication.h new file mode 100644 index 000000000..2a2fc23cb --- /dev/null +++ b/Source/JavaScriptCore/wtf/qt/compat/qguiapplication.h @@ -0,0 +1,35 @@ +/* + Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies) + + This library 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 library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef qguiapplication_h +#define qguiapplication_h + +#include <QApplication> + +struct QGuiApplication : public QApplication +{ + // Style hints in Qt 5 contain stuff that just used to be in QApplication in Qt 4, hence + // this hack. + static QApplication* styleHints() + { + return qApp; + } +}; + +#endif diff --git a/Source/JavaScriptCore/wtf/text/ASCIIFastPath.h b/Source/JavaScriptCore/wtf/text/ASCIIFastPath.h new file mode 100644 index 000000000..ace1a687d --- /dev/null +++ b/Source/JavaScriptCore/wtf/text/ASCIIFastPath.h @@ -0,0 +1,101 @@ +/* + * Copyright (C) 2011 Apple Inc. All rights reserved. + * Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). + * + * This library 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 library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#ifndef ASCIIFastPath_h +#define ASCIIFastPath_h + +#include <stdint.h> +#include <wtf/unicode/Unicode.h> + +namespace WTF { + +// Assuming that a pointer is the size of a "machine word", then +// uintptr_t is an integer type that is also a machine word. +typedef uintptr_t MachineWord; +const uintptr_t machineWordAlignmentMask = sizeof(MachineWord) - 1; + +inline bool isAlignedToMachineWord(const void* pointer) +{ + return !(reinterpret_cast<uintptr_t>(pointer) & machineWordAlignmentMask); +} + +template<typename T> inline T* alignToMachineWord(T* pointer) +{ + return reinterpret_cast<T*>(reinterpret_cast<uintptr_t>(pointer) & ~machineWordAlignmentMask); +} + +template<size_t size, typename CharacterType> struct NonASCIIMask; +template<> struct NonASCIIMask<4, UChar> { + static inline uint32_t value() { return 0xFF80FF80U; } +}; +template<> struct NonASCIIMask<4, LChar> { + static inline uint32_t value() { return 0x80808080U; } +}; +template<> struct NonASCIIMask<8, UChar> { + static inline uint64_t value() { return 0xFF80FF80FF80FF80ULL; } +}; +template<> struct NonASCIIMask<8, LChar> { + static inline uint64_t value() { return 0x8080808080808080ULL; } +}; + + +template<typename CharacterType> +inline bool isAllASCII(MachineWord word) +{ + return !(word & NonASCIIMask<sizeof(MachineWord), CharacterType>::value()); +} + +// Note: This function assume the input is likely all ASCII, and +// does not leave early if it is not the case. +template<typename CharacterType> +inline bool charactersAreAllASCII(const CharacterType* characters, size_t length) +{ + MachineWord allCharBits = 0; + const CharacterType* end = characters + length; + + // Prologue: align the input. + while (!isAlignedToMachineWord(characters) && characters != end) { + allCharBits |= *characters; + ++characters; + } + + // Compare the values of CPU word size. + const CharacterType* wordEnd = alignToMachineWord(end); + const size_t loopIncrement = sizeof(MachineWord) / sizeof(CharacterType); + while (characters < wordEnd) { + allCharBits |= *(reinterpret_cast<const MachineWord*>(characters)); + characters += loopIncrement; + } + + // Process the remaining bytes. + while (characters != end) { + allCharBits |= *characters; + ++characters; + } + + MachineWord nonASCIIBitMask = NonASCIIMask<sizeof(MachineWord), CharacterType>::value(); + return !(allCharBits & nonASCIIBitMask); +} + + +} // namespace WTF + +#endif // ASCIIFastPath_h diff --git a/Source/JavaScriptCore/wtf/text/AtomicString.cpp b/Source/JavaScriptCore/wtf/text/AtomicString.cpp new file mode 100644 index 000000000..966879827 --- /dev/null +++ b/Source/JavaScriptCore/wtf/text/AtomicString.cpp @@ -0,0 +1,320 @@ +/* + * Copyright (C) 2004, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved. + * Copyright (C) 2010 Patrick Gansterer <paroga@paroga.com> + * + * This library 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 library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#include "config.h" + +#include "AtomicString.h" + +#include "StringHash.h" +#include <wtf/HashSet.h> +#include <wtf/Threading.h> +#include <wtf/WTFThreadData.h> +#include <wtf/unicode/UTF8.h> + +namespace WTF { + +using namespace Unicode; + +COMPILE_ASSERT(sizeof(AtomicString) == sizeof(String), atomic_string_and_string_must_be_same_size); + +class AtomicStringTable { +public: + static AtomicStringTable* create() + { + AtomicStringTable* table = new AtomicStringTable; + + WTFThreadData& data = wtfThreadData(); + data.m_atomicStringTable = table; + data.m_atomicStringTableDestructor = AtomicStringTable::destroy; + + return table; + } + + HashSet<StringImpl*>& table() + { + return m_table; + } + +private: + static void destroy(AtomicStringTable* table) + { + HashSet<StringImpl*>::iterator end = table->m_table.end(); + for (HashSet<StringImpl*>::iterator iter = table->m_table.begin(); iter != end; ++iter) + (*iter)->setIsAtomic(false); + delete table; + } + + HashSet<StringImpl*> m_table; +}; + +static inline HashSet<StringImpl*>& stringTable() +{ + // Once possible we should make this non-lazy (constructed in WTFThreadData's constructor). + AtomicStringTable* table = wtfThreadData().atomicStringTable(); + if (UNLIKELY(!table)) + table = AtomicStringTable::create(); + return table->table(); +} + +template<typename T, typename HashTranslator> +static inline PassRefPtr<StringImpl> addToStringTable(const T& value) +{ + pair<HashSet<StringImpl*>::iterator, bool> addResult = stringTable().add<T, HashTranslator>(value); + + // If the string is newly-translated, then we need to adopt it. + // The boolean in the pair tells us if that is so. + return addResult.second ? adoptRef(*addResult.first) : *addResult.first; +} + +struct CStringTranslator { + static unsigned hash(const LChar* c) + { + return StringHasher::computeHash(c); + } + + static inline bool equal(StringImpl* r, const LChar* s) + { + return WTF::equal(r, s); + } + + static void translate(StringImpl*& location, const LChar* const& c, unsigned hash) + { + location = StringImpl::create(c).leakRef(); + location->setHash(hash); + location->setIsAtomic(true); + } +}; + +PassRefPtr<StringImpl> AtomicString::add(const LChar* c) +{ + if (!c) + return 0; + if (!*c) + return StringImpl::empty(); + + return addToStringTable<const LChar*, CStringTranslator>(c); +} + +struct UCharBuffer { + const UChar* s; + unsigned length; +}; + +struct UCharBufferTranslator { + static unsigned hash(const UCharBuffer& buf) + { + return StringHasher::computeHash(buf.s, buf.length); + } + + static bool equal(StringImpl* const& str, const UCharBuffer& buf) + { + return WTF::equal(str, buf.s, buf.length); + } + + static void translate(StringImpl*& location, const UCharBuffer& buf, unsigned hash) + { + location = StringImpl::create(buf.s, buf.length).leakRef(); + location->setHash(hash); + location->setIsAtomic(true); + } +}; + +struct HashAndCharacters { + unsigned hash; + const UChar* characters; + unsigned length; +}; + +struct HashAndCharactersTranslator { + static unsigned hash(const HashAndCharacters& buffer) + { + ASSERT(buffer.hash == StringHasher::computeHash(buffer.characters, buffer.length)); + return buffer.hash; + } + + static bool equal(StringImpl* const& string, const HashAndCharacters& buffer) + { + return WTF::equal(string, buffer.characters, buffer.length); + } + + static void translate(StringImpl*& location, const HashAndCharacters& buffer, unsigned hash) + { + location = StringImpl::create(buffer.characters, buffer.length).leakRef(); + location->setHash(hash); + location->setIsAtomic(true); + } +}; + +struct HashAndUTF8Characters { + unsigned hash; + const char* characters; + unsigned length; + unsigned utf16Length; +}; + +struct HashAndUTF8CharactersTranslator { + static unsigned hash(const HashAndUTF8Characters& buffer) + { + return buffer.hash; + } + + static bool equal(StringImpl* const& string, const HashAndUTF8Characters& buffer) + { + if (buffer.utf16Length != string->length()) + return false; + + const UChar* stringCharacters = string->characters(); + + // If buffer contains only ASCII characters UTF-8 and UTF16 length are the same. + if (buffer.utf16Length != buffer.length) + return equalUTF16WithUTF8(stringCharacters, stringCharacters + string->length(), buffer.characters, buffer.characters + buffer.length); + + for (unsigned i = 0; i < buffer.length; ++i) { + ASSERT(isASCII(buffer.characters[i])); + if (stringCharacters[i] != buffer.characters[i]) + return false; + } + + return true; + } + + static void translate(StringImpl*& location, const HashAndUTF8Characters& buffer, unsigned hash) + { + UChar* target; + location = StringImpl::createUninitialized(buffer.utf16Length, target).leakRef(); + + const char* source = buffer.characters; + if (convertUTF8ToUTF16(&source, source + buffer.length, &target, target + buffer.utf16Length) != conversionOK) + ASSERT_NOT_REACHED(); + + location->setHash(hash); + location->setIsAtomic(true); + } +}; + +PassRefPtr<StringImpl> AtomicString::add(const UChar* s, unsigned length) +{ + if (!s) + return 0; + + if (!length) + return StringImpl::empty(); + + UCharBuffer buffer = { s, length }; + return addToStringTable<UCharBuffer, UCharBufferTranslator>(buffer); +} + +PassRefPtr<StringImpl> AtomicString::add(const UChar* s, unsigned length, unsigned existingHash) +{ + ASSERT(s); + ASSERT(existingHash); + + if (!length) + return StringImpl::empty(); + + HashAndCharacters buffer = { existingHash, s, length }; + return addToStringTable<HashAndCharacters, HashAndCharactersTranslator>(buffer); +} + +PassRefPtr<StringImpl> AtomicString::add(const UChar* s) +{ + if (!s) + return 0; + + int length = 0; + while (s[length] != UChar(0)) + length++; + + if (!length) + return StringImpl::empty(); + + UCharBuffer buffer = { s, length }; + return addToStringTable<UCharBuffer, UCharBufferTranslator>(buffer); +} + +PassRefPtr<StringImpl> AtomicString::addSlowCase(StringImpl* r) +{ + if (!r || r->isAtomic()) + return r; + + if (!r->length()) + return StringImpl::empty(); + + StringImpl* result = *stringTable().add(r).first; + if (result == r) + r->setIsAtomic(true); + return result; +} + +AtomicStringImpl* AtomicString::find(const UChar* s, unsigned length, unsigned existingHash) +{ + ASSERT(s); + ASSERT(existingHash); + + if (!length) + return static_cast<AtomicStringImpl*>(StringImpl::empty()); + + HashAndCharacters buffer = { existingHash, s, length }; + HashSet<StringImpl*>::iterator iterator = stringTable().find<HashAndCharacters, HashAndCharactersTranslator>(buffer); + if (iterator == stringTable().end()) + return 0; + return static_cast<AtomicStringImpl*>(*iterator); +} + +void AtomicString::remove(StringImpl* r) +{ + stringTable().remove(r); +} + +AtomicString AtomicString::lower() const +{ + // Note: This is a hot function in the Dromaeo benchmark. + StringImpl* impl = this->impl(); + if (UNLIKELY(!impl)) + return *this; + RefPtr<StringImpl> newImpl = impl->lower(); + if (LIKELY(newImpl == impl)) + return *this; + return AtomicString(newImpl); +} + +AtomicString AtomicString::fromUTF8Internal(const char* charactersStart, const char* charactersEnd) +{ + HashAndUTF8Characters buffer; + buffer.characters = charactersStart; + buffer.hash = calculateStringHashAndLengthFromUTF8(charactersStart, charactersEnd, buffer.length, buffer.utf16Length); + + if (!buffer.hash) + return nullAtom; + + AtomicString atomicString; + atomicString.m_string = addToStringTable<HashAndUTF8Characters, HashAndUTF8CharactersTranslator>(buffer); + return atomicString; +} + +#ifndef NDEBUG +void AtomicString::show() const +{ + m_string.show(); +} +#endif + +} // namespace WTF diff --git a/Source/JavaScriptCore/wtf/text/AtomicString.h b/Source/JavaScriptCore/wtf/text/AtomicString.h new file mode 100644 index 000000000..43b38d179 --- /dev/null +++ b/Source/JavaScriptCore/wtf/text/AtomicString.h @@ -0,0 +1,215 @@ +/* + * Copyright (C) 2004, 2005, 2006, 2008 Apple Inc. All rights reserved. + * + * This library 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 library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#ifndef AtomicString_h +#define AtomicString_h + +#include "AtomicStringImpl.h" +#include "WTFString.h" + +// Define 'NO_IMPLICIT_ATOMICSTRING' before including this header, +// to disallow (expensive) implicit String-->AtomicString conversions. +#ifdef NO_IMPLICIT_ATOMICSTRING +#define ATOMICSTRING_CONVERSION explicit +#else +#define ATOMICSTRING_CONVERSION +#endif + +namespace WTF { + +struct AtomicStringHash; + +class AtomicString { +public: + static void init(); + + AtomicString() { } + AtomicString(const LChar* s) : m_string(add(s)) { } + AtomicString(const char* s) : m_string(add(s)) { } + AtomicString(const UChar* s, unsigned length) : m_string(add(s, length)) { } + AtomicString(const UChar* s, unsigned length, unsigned existingHash) : m_string(add(s, length, existingHash)) { } + AtomicString(const UChar* s) : m_string(add(s)) { } + ATOMICSTRING_CONVERSION AtomicString(StringImpl* imp) : m_string(add(imp)) { } + AtomicString(AtomicStringImpl* imp) : m_string(imp) { } + ATOMICSTRING_CONVERSION AtomicString(const String& s) : m_string(add(s.impl())) { } + + // Hash table deleted values, which are only constructed and never copied or destroyed. + AtomicString(WTF::HashTableDeletedValueType) : m_string(WTF::HashTableDeletedValue) { } + bool isHashTableDeletedValue() const { return m_string.isHashTableDeletedValue(); } + + static AtomicStringImpl* find(const UChar* s, unsigned length, unsigned existingHash); + + operator const String&() const { return m_string; } + const String& string() const { return m_string; }; + + AtomicStringImpl* impl() const { return static_cast<AtomicStringImpl *>(m_string.impl()); } + + const UChar* characters() const { return m_string.characters(); } + unsigned length() const { return m_string.length(); } + + UChar operator[](unsigned int i) const { return m_string[i]; } + + bool contains(UChar c) const { return m_string.contains(c); } + bool contains(const LChar* s, bool caseSensitive = true) const + { return m_string.contains(s, caseSensitive); } + bool contains(const String& s, bool caseSensitive = true) const + { return m_string.contains(s, caseSensitive); } + + size_t find(UChar c, size_t start = 0) const { return m_string.find(c, start); } + size_t find(const LChar* s, size_t start = 0, bool caseSentitive = true) const + { return m_string.find(s, start, caseSentitive); } + size_t find(const String& s, size_t start = 0, bool caseSentitive = true) const + { return m_string.find(s, start, caseSentitive); } + + bool startsWith(const String& s, bool caseSensitive = true) const + { return m_string.startsWith(s, caseSensitive); } + bool endsWith(const String& s, bool caseSensitive = true) const + { return m_string.endsWith(s, caseSensitive); } + + AtomicString lower() const; + AtomicString upper() const { return AtomicString(impl()->upper()); } + + int toInt(bool* ok = 0) const { return m_string.toInt(ok); } + double toDouble(bool* ok = 0) const { return m_string.toDouble(ok); } + float toFloat(bool* ok = 0) const { return m_string.toFloat(ok); } + bool percentage(int& p) const { return m_string.percentage(p); } + + bool isNull() const { return m_string.isNull(); } + bool isEmpty() const { return m_string.isEmpty(); } + + static void remove(StringImpl*); + +#if USE(CF) + AtomicString(CFStringRef s) : m_string(add(String(s).impl())) { } + CFStringRef createCFString() const { return m_string.createCFString(); } +#endif +#ifdef __OBJC__ + AtomicString(NSString* s) : m_string(add(String(s).impl())) { } + operator NSString*() const { return m_string; } +#endif +#if PLATFORM(QT) + AtomicString(const QString& s) : m_string(add(String(s).impl())) { } + operator QString() const { return m_string; } +#endif + + // AtomicString::fromUTF8 will return a null string if + // the input data contains invalid UTF-8 sequences. + static AtomicString fromUTF8(const char*, size_t); + static AtomicString fromUTF8(const char*); + +#ifndef NDEBUG + void show() const; +#endif +private: + String m_string; + + static PassRefPtr<StringImpl> add(const LChar*); + ALWAYS_INLINE static PassRefPtr<StringImpl> add(const char* s) { return add(reinterpret_cast<const LChar*>(s)); }; + static PassRefPtr<StringImpl> add(const UChar*, unsigned length); + ALWAYS_INLINE static PassRefPtr<StringImpl> add(const char* s, unsigned length) { return add(reinterpret_cast<const char*>(s), length); }; + static PassRefPtr<StringImpl> add(const UChar*, unsigned length, unsigned existingHash); + static PassRefPtr<StringImpl> add(const UChar*); + ALWAYS_INLINE PassRefPtr<StringImpl> add(StringImpl* r) + { + if (!r || r->isAtomic()) + return r; + return addSlowCase(r); + } + static PassRefPtr<StringImpl> addSlowCase(StringImpl*); + static AtomicString fromUTF8Internal(const char*, const char*); +}; + +inline bool operator==(const AtomicString& a, const AtomicString& b) { return a.impl() == b.impl(); } +bool operator==(const AtomicString&, const LChar*); +inline bool operator==(const AtomicString& a, const char* b) { return WTF::equal(a.impl(), reinterpret_cast<const LChar*>(b)); } +inline bool operator==(const AtomicString& a, const Vector<UChar>& b) { return a.impl() && equal(a.impl(), b.data(), b.size()); } +inline bool operator==(const AtomicString& a, const String& b) { return equal(a.impl(), b.impl()); } +inline bool operator==(const LChar* a, const AtomicString& b) { return b == a; } +inline bool operator==(const String& a, const AtomicString& b) { return equal(a.impl(), b.impl()); } +inline bool operator==(const Vector<UChar>& a, const AtomicString& b) { return b == a; } + +inline bool operator!=(const AtomicString& a, const AtomicString& b) { return a.impl() != b.impl(); } +inline bool operator!=(const AtomicString& a, const LChar* b) { return !(a == b); } +inline bool operator!=(const AtomicString& a, const char* b) { return !(a == b); } +inline bool operator!=(const AtomicString& a, const String& b) { return !equal(a.impl(), b.impl()); } +inline bool operator!=(const AtomicString& a, const Vector<UChar>& b) { return !(a == b); } +inline bool operator!=(const LChar* a, const AtomicString& b) { return !(b == a); } +inline bool operator!=(const String& a, const AtomicString& b) { return !equal(a.impl(), b.impl()); } +inline bool operator!=(const Vector<UChar>& a, const AtomicString& b) { return !(a == b); } + +inline bool equalIgnoringCase(const AtomicString& a, const AtomicString& b) { return equalIgnoringCase(a.impl(), b.impl()); } +inline bool equalIgnoringCase(const AtomicString& a, const LChar* b) { return equalIgnoringCase(a.impl(), b); } +inline bool equalIgnoringCase(const AtomicString& a, const char* b) { return equalIgnoringCase(a.impl(), reinterpret_cast<const LChar*>(b)); } +inline bool equalIgnoringCase(const AtomicString& a, const String& b) { return equalIgnoringCase(a.impl(), b.impl()); } +inline bool equalIgnoringCase(const LChar* a, const AtomicString& b) { return equalIgnoringCase(a, b.impl()); } +inline bool equalIgnoringCase(const char* a, const AtomicString& b) { return equalIgnoringCase(reinterpret_cast<const LChar*>(a), b.impl()); } +inline bool equalIgnoringCase(const String& a, const AtomicString& b) { return equalIgnoringCase(a.impl(), b.impl()); } + +// Define external global variables for the commonly used atomic strings. +// These are only usable from the main thread. +#ifndef ATOMICSTRING_HIDE_GLOBALS +extern const WTF_EXPORTDATA AtomicString nullAtom; +extern const WTF_EXPORTDATA AtomicString emptyAtom; +extern const WTF_EXPORTDATA AtomicString textAtom; +extern const WTF_EXPORTDATA AtomicString commentAtom; +extern const WTF_EXPORTDATA AtomicString starAtom; +extern const WTF_EXPORTDATA AtomicString xmlAtom; +extern const WTF_EXPORTDATA AtomicString xmlnsAtom; + +inline AtomicString AtomicString::fromUTF8(const char* characters, size_t length) +{ + if (!characters) + return nullAtom; + if (!length) + return emptyAtom; + return fromUTF8Internal(characters, characters + length); +} + +inline AtomicString AtomicString::fromUTF8(const char* characters) +{ + if (!characters) + return nullAtom; + if (!*characters) + return emptyAtom; + return fromUTF8Internal(characters, 0); +} +#endif + +// AtomicStringHash is the default hash for AtomicString +template<typename T> struct DefaultHash; +template<> struct DefaultHash<AtomicString> { + typedef AtomicStringHash Hash; +}; + +} // namespace WTF + +#ifndef ATOMICSTRING_HIDE_GLOBALS +using WTF::AtomicString; +using WTF::nullAtom; +using WTF::emptyAtom; +using WTF::textAtom; +using WTF::commentAtom; +using WTF::starAtom; +using WTF::xmlAtom; +using WTF::xmlnsAtom; +#endif + +#include "StringConcatenate.h" +#endif // AtomicString_h diff --git a/Source/JavaScriptCore/wtf/text/AtomicStringHash.h b/Source/JavaScriptCore/wtf/text/AtomicStringHash.h new file mode 100644 index 000000000..6130d9493 --- /dev/null +++ b/Source/JavaScriptCore/wtf/text/AtomicStringHash.h @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2008 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of Apple Computer, Inc. ("Apple") 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 APPLE AND ITS 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 APPLE OR ITS 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. + */ + +#ifndef AtomicStringHash_h +#define AtomicStringHash_h + +#include <wtf/text/AtomicString.h> +#include <wtf/HashTraits.h> + +namespace WTF { + + struct AtomicStringHash { + static unsigned hash(const AtomicString& key) + { + return key.impl()->existingHash(); + } + + static bool equal(const AtomicString& a, const AtomicString& b) + { + return a == b; + } + + static const bool safeToCompareToEmptyOrDeleted = false; + }; + + // AtomicStringHash is the default hash for AtomicString + template<> struct HashTraits<WTF::AtomicString> : GenericHashTraits<WTF::AtomicString> { + static const bool emptyValueIsZero = true; + static void constructDeletedValue(WTF::AtomicString& slot) { new (NotNull, &slot) WTF::AtomicString(HashTableDeletedValue); } + static bool isDeletedValue(const WTF::AtomicString& slot) { return slot.isHashTableDeletedValue(); } + }; + +} + +using WTF::AtomicStringHash; + +#endif diff --git a/Source/JavaScriptCore/wtf/text/AtomicStringImpl.h b/Source/JavaScriptCore/wtf/text/AtomicStringImpl.h new file mode 100644 index 000000000..3f0c37606 --- /dev/null +++ b/Source/JavaScriptCore/wtf/text/AtomicStringImpl.h @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2006 Apple Computer, Inc. + * + * This library 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 library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#ifndef AtomicStringImpl_h +#define AtomicStringImpl_h + +#include "StringImpl.h" + +namespace WTF { + +class AtomicStringImpl : public StringImpl +{ +public: + AtomicStringImpl() : StringImpl(0) {} +}; + +} + +using WTF::AtomicStringImpl; + +#endif diff --git a/Source/JavaScriptCore/wtf/text/CString.cpp b/Source/JavaScriptCore/wtf/text/CString.cpp new file mode 100644 index 000000000..981d77a1d --- /dev/null +++ b/Source/JavaScriptCore/wtf/text/CString.cpp @@ -0,0 +1,104 @@ +/* + * Copyright (C) 2003, 2006, 2008, 2009, 2010 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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 APPLE COMPUTER, INC. ``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 APPLE COMPUTER, INC. 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 "config.h" +#include "CString.h" + +using namespace std; + +namespace WTF { + +CString::CString(const char* str) +{ + if (!str) + return; + + init(str, strlen(str)); +} + +CString::CString(const char* str, size_t length) +{ + init(str, length); +} + +void CString::init(const char* str, size_t length) +{ + if (!str) + return; + + // We need to be sure we can add 1 to length without overflowing. + // Since the passed-in length is the length of an actual existing + // string, and we know the string doesn't occupy the entire address + // space, we can assert here and there's no need for a runtime check. + ASSERT(length < numeric_limits<size_t>::max()); + + m_buffer = CStringBuffer::create(length + 1); + memcpy(m_buffer->mutableData(), str, length); + m_buffer->mutableData()[length] = '\0'; +} + +char* CString::mutableData() +{ + copyBufferIfNeeded(); + if (!m_buffer) + return 0; + return m_buffer->mutableData(); +} + +CString CString::newUninitialized(size_t length, char*& characterBuffer) +{ + if (length >= numeric_limits<size_t>::max()) + CRASH(); + + CString result; + result.m_buffer = CStringBuffer::create(length + 1); + char* bytes = result.m_buffer->mutableData(); + bytes[length] = '\0'; + characterBuffer = bytes; + return result; +} + +void CString::copyBufferIfNeeded() +{ + if (!m_buffer || m_buffer->hasOneRef()) + return; + + RefPtr<CStringBuffer> buffer = m_buffer.release(); + size_t length = buffer->length(); + m_buffer = CStringBuffer::create(length); + memcpy(m_buffer->mutableData(), buffer->data(), length); +} + +bool operator==(const CString& a, const CString& b) +{ + if (a.isNull() != b.isNull()) + return false; + if (a.length() != b.length()) + return false; + return !strncmp(a.data(), b.data(), min(a.length(), b.length())); +} + +} // namespace WTF diff --git a/Source/JavaScriptCore/wtf/text/CString.h b/Source/JavaScriptCore/wtf/text/CString.h new file mode 100644 index 000000000..343a7a525 --- /dev/null +++ b/Source/JavaScriptCore/wtf/text/CString.h @@ -0,0 +1,87 @@ +/* + * Copyright (C) 2003, 2006, 2008, 2009, 2010 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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 APPLE COMPUTER, INC. ``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 APPLE COMPUTER, INC. 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. + */ + +#ifndef CString_h +#define CString_h + +#include "PassRefPtr.h" +#include "RefCounted.h" +#include "Vector.h" + +namespace WTF { + +class CStringBuffer : public RefCounted<CStringBuffer> { +public: + const char* data() { return m_vector.data(); } + size_t length() { return m_vector.size(); } + +private: + friend class CString; + + static PassRefPtr<CStringBuffer> create(size_t length) { return adoptRef(new CStringBuffer(length)); } + CStringBuffer(size_t length) : m_vector(length) { } + char* mutableData() { return m_vector.data(); } + + Vector<char> m_vector; +}; + +// A container for a null-terminated char array supporting copy-on-write +// assignment. The contained char array may be null. +class CString { +public: + CString() { } + CString(const char*); + CString(const char*, size_t length); + CString(CStringBuffer* buffer) : m_buffer(buffer) { } + static CString newUninitialized(size_t length, char*& characterBuffer); + + const char* data() const + { + return m_buffer ? m_buffer->data() : 0; + } + char* mutableData(); + size_t length() const + { + return m_buffer ? m_buffer->length() - 1 : 0; + } + + bool isNull() const { return !m_buffer; } + + CStringBuffer* buffer() const { return m_buffer.get(); } + +private: + void copyBufferIfNeeded(); + void init(const char*, size_t length); + RefPtr<CStringBuffer> m_buffer; +}; + +bool operator==(const CString& a, const CString& b); +inline bool operator!=(const CString& a, const CString& b) { return !(a == b); } + +} // namespace WTF + +using WTF::CString; + +#endif // CString_h diff --git a/Source/JavaScriptCore/wtf/text/StringBuffer.h b/Source/JavaScriptCore/wtf/text/StringBuffer.h new file mode 100644 index 000000000..739260d27 --- /dev/null +++ b/Source/JavaScriptCore/wtf/text/StringBuffer.h @@ -0,0 +1,87 @@ +/* + * Copyright (C) 2008, 2010 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of Apple Inc. ("Apple") 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 APPLE AND ITS 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 APPLE OR ITS 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. + */ + +#ifndef StringBuffer_h +#define StringBuffer_h + +#include <wtf/Assertions.h> +#include <wtf/unicode/Unicode.h> +#include <limits> + +namespace WTF { + +template <typename CharType> +class StringBuffer { + WTF_MAKE_NONCOPYABLE(StringBuffer); +public: + explicit StringBuffer(unsigned length) + : m_length(length) + { + if (m_length > std::numeric_limits<unsigned>::max() / sizeof(CharType)) + CRASH(); + m_data = static_cast<CharType*>(fastMalloc(m_length * sizeof(CharType))); + } + + ~StringBuffer() + { + fastFree(m_data); + } + + void shrink(unsigned newLength) + { + ASSERT(newLength <= m_length); + m_length = newLength; + } + + void resize(unsigned newLength) + { + if (newLength > m_length) { + if (newLength > std::numeric_limits<unsigned>::max() / sizeof(UChar)) + CRASH(); + m_data = static_cast<UChar*>(fastRealloc(m_data, newLength * sizeof(UChar))); + } + m_length = newLength; + } + + unsigned length() const { return m_length; } + CharType* characters() { return m_data; } + + UChar& operator[](unsigned i) { ASSERT(i < m_length); return m_data[i]; } + + CharType* release() { CharType* data = m_data; m_data = 0; return data; } + +private: + unsigned m_length; + CharType* m_data; +}; + +} // namespace WTF + +using WTF::StringBuffer; + +#endif // StringBuffer_h diff --git a/Source/JavaScriptCore/wtf/text/StringBuilder.cpp b/Source/JavaScriptCore/wtf/text/StringBuilder.cpp new file mode 100644 index 000000000..6d3c310e6 --- /dev/null +++ b/Source/JavaScriptCore/wtf/text/StringBuilder.cpp @@ -0,0 +1,301 @@ +/* + * Copyright (C) 2010 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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 APPLE INC. ``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 APPLE INC. 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 "config.h" +#include "StringBuilder.h" + +#include "WTFString.h" + +namespace WTF { + +static const unsigned minimumCapacity = 16; + +void StringBuilder::reifyString() +{ + // Check if the string already exists. + if (!m_string.isNull()) { + ASSERT(m_string.length() == m_length); + return; + } + + // Check for empty. + if (!m_length) { + m_string = StringImpl::empty(); + return; + } + + // Must be valid in the buffer, take a substring (unless string fills the buffer). + ASSERT(m_buffer && m_length <= m_buffer->length()); + m_string = (m_length == m_buffer->length()) + ? m_buffer.get() + : StringImpl::create(m_buffer, 0, m_length); + + if (m_buffer->has16BitShadow() && m_valid16BitShadowLength < m_length) + m_buffer->upconvertCharacters(m_valid16BitShadowLength, m_length); + + m_valid16BitShadowLength = m_length; +} + +void StringBuilder::resize(unsigned newSize) +{ + // Check newSize < m_length, hence m_length > 0. + ASSERT(newSize <= m_length); + if (newSize == m_length) + return; + ASSERT(m_length); + + // If there is a buffer, we only need to duplicate it if it has more than one ref. + if (m_buffer) { + if (!m_buffer->hasOneRef()) { + if (m_buffer->is8Bit()) + allocateBuffer(m_buffer->characters8(), m_buffer->length()); + else + allocateBuffer(m_buffer->characters16(), m_buffer->length()); + } + m_length = newSize; + m_string = String(); + return; + } + + // Since m_length && !m_buffer, the string must be valid in m_string, and m_string.length() > 0. + ASSERT(!m_string.isEmpty()); + ASSERT(m_length == m_string.length()); + ASSERT(newSize < m_string.length()); + m_length = newSize; + m_string = StringImpl::create(m_string.impl(), 0, newSize); +} + +// Allocate a new 8 bit buffer, copying in currentCharacters (these may come from either m_string +// or m_buffer, neither will be reassigned until the copy has completed). +void StringBuilder::allocateBuffer(const LChar* currentCharacters, unsigned requiredLength) +{ + ASSERT(m_is8Bit); + // Copy the existing data into a new buffer, set result to point to the end of the existing data. + RefPtr<StringImpl> buffer = StringImpl::createUninitialized(requiredLength, m_bufferCharacters8); + memcpy(m_bufferCharacters8, currentCharacters, static_cast<size_t>(m_length) * sizeof(LChar)); // This can't overflow. + + // Update the builder state. + m_buffer = buffer.release(); + m_string = String(); +} + +// Allocate a new 16 bit buffer, copying in currentCharacters (these may come from either m_string +// or m_buffer, neither will be reassigned until the copy has completed). +void StringBuilder::allocateBuffer(const UChar* currentCharacters, unsigned requiredLength) +{ + ASSERT(!m_is8Bit); + // Copy the existing data into a new buffer, set result to point to the end of the existing data. + RefPtr<StringImpl> buffer = StringImpl::createUninitialized(requiredLength, m_bufferCharacters16); + memcpy(m_bufferCharacters16, currentCharacters, static_cast<size_t>(m_length) * sizeof(UChar)); // This can't overflow. + + // Update the builder state. + m_buffer = buffer.release(); + m_string = String(); +} + +// Allocate a new 16 bit buffer, copying in currentCharacters (which is 8 bit and may come +// from either m_string or m_buffer, neither will be reassigned until the copy has completed). +void StringBuilder::allocateBufferUpConvert(const LChar* currentCharacters, unsigned requiredLength) +{ + ASSERT(m_is8Bit); + // Copy the existing data into a new buffer, set result to point to the end of the existing data. + RefPtr<StringImpl> buffer = StringImpl::createUninitialized(requiredLength, m_bufferCharacters16); + for (unsigned i = 0; i < m_length; i++) + m_bufferCharacters16[i] = currentCharacters[i]; + + m_is8Bit = false; + + // Update the builder state. + m_buffer = buffer.release(); + m_string = String(); +} + +template <> +void StringBuilder::reallocateBuffer<LChar>(unsigned requiredLength) +{ + // If the buffer has only one ref (by this StringBuilder), reallocate it, + // otherwise fall back to "allocate and copy" method. + m_string = String(); + + ASSERT(m_is8Bit); + ASSERT(m_buffer->is8Bit()); + + if (m_buffer->hasOneRef()) + m_buffer = StringImpl::reallocate(m_buffer.release(), requiredLength, m_bufferCharacters8); + else + allocateBuffer(m_buffer->characters8(), requiredLength); +} + +template <> +void StringBuilder::reallocateBuffer<UChar>(unsigned requiredLength) +{ + // If the buffer has only one ref (by this StringBuilder), reallocate it, + // otherwise fall back to "allocate and copy" method. + m_string = String(); + + if (m_buffer->is8Bit()) + allocateBufferUpConvert(m_buffer->characters8(), requiredLength); + else if (m_buffer->hasOneRef()) + m_buffer = StringImpl::reallocate(m_buffer.release(), requiredLength, m_bufferCharacters16); + else + allocateBuffer(m_buffer->characters16(), requiredLength); +} + +void StringBuilder::reserveCapacity(unsigned newCapacity) +{ + if (m_buffer) { + // If there is already a buffer, then grow if necessary. + if (newCapacity > m_buffer->length()) { + if (m_buffer->is8Bit()) + reallocateBuffer<LChar>(newCapacity); + else + reallocateBuffer<UChar>(newCapacity); + } + } else { + // Grow the string, if necessary. + if (newCapacity > m_length) { + if (!m_length) { + LChar* nullPlaceholder = 0; + allocateBuffer(nullPlaceholder, newCapacity); + } else if (m_string.is8Bit()) + allocateBuffer(m_string.characters8(), newCapacity); + else + allocateBuffer(m_string.characters16(), newCapacity); + } + } +} + +// Make 'length' additional capacity be available in m_buffer, update m_string & m_length, +// return a pointer to the newly allocated storage. +template <typename CharType> +ALWAYS_INLINE CharType* StringBuilder::appendUninitialized(unsigned length) +{ + ASSERT(length); + + // Calculate the new size of the builder after appending. + unsigned requiredLength = length + m_length; + if (requiredLength < length) + CRASH(); + + if ((m_buffer) && (requiredLength <= m_buffer->length())) { + // If the buffer is valid it must be at least as long as the current builder contents! + ASSERT(m_buffer->length() >= m_length); + unsigned currentLength = m_length; + m_string = String(); + m_length = requiredLength; + return getBufferCharacters<CharType>() + currentLength; + } + + return appendUninitializedSlow<CharType>(requiredLength); +} + +// Make 'length' additional capacity be available in m_buffer, update m_string & m_length, +// return a pointer to the newly allocated storage. +template <typename CharType> +CharType* StringBuilder::appendUninitializedSlow(unsigned requiredLength) +{ + ASSERT(requiredLength); + + if (m_buffer) { + // If the buffer is valid it must be at least as long as the current builder contents! + ASSERT(m_buffer->length() >= m_length); + + reallocateBuffer<CharType>(std::max(requiredLength, std::max(minimumCapacity, m_buffer->length() * 2))); + } else { + ASSERT(m_string.length() == m_length); + allocateBuffer(m_length ? m_string.getCharacters<CharType>() : 0, std::max(requiredLength, std::max(minimumCapacity, m_length * 2))); + } + + CharType* result = getBufferCharacters<CharType>() + m_length; + m_length = requiredLength; + return result; +} + +void StringBuilder::append(const UChar* characters, unsigned length) +{ + if (!length) + return; + + ASSERT(characters); + + if (m_is8Bit) { + // Calculate the new size of the builder after appending. + unsigned requiredLength = length + m_length; + if (requiredLength < length) + CRASH(); + + if (m_buffer) { + // If the buffer is valid it must be at least as long as the current builder contents! + ASSERT(m_buffer->length() >= m_length); + + allocateBufferUpConvert(m_buffer->characters8(), requiredLength); + } else { + ASSERT(m_string.length() == m_length); + allocateBufferUpConvert(m_string.isNull() ? 0 : m_string.characters8(), std::max(requiredLength, std::max(minimumCapacity, m_length * 2))); + } + + memcpy(m_bufferCharacters16 + m_length, characters, static_cast<size_t>(length) * sizeof(UChar)); + m_length = requiredLength; + } else + memcpy(appendUninitialized<UChar>(length), characters, static_cast<size_t>(length) * sizeof(UChar)); +} + +void StringBuilder::append(const LChar* characters, unsigned length) +{ + if (!length) + return; + ASSERT(characters); + + if (m_is8Bit) { + LChar* dest = appendUninitialized<LChar>(length); + if (length > 8) + memcpy(dest, characters, static_cast<size_t>(length) * sizeof(LChar)); + else { + const LChar* end = characters + length; + while (characters < end) + *(dest++) = *(characters++); + } + } else { + UChar* dest = appendUninitialized<UChar>(length); + const LChar* end = characters + length; + while (characters < end) + *(dest++) = *(characters++); + } +} + +void StringBuilder::shrinkToFit() +{ + // If the buffer is at least 80% full, don't bother copying. Need to tune this heuristic! + if (m_buffer && m_buffer->length() > (m_length + (m_length >> 2))) { + if (m_is8Bit) + reallocateBuffer<LChar>(m_length); + else + reallocateBuffer<UChar>(m_length); + m_string = m_buffer; + m_buffer = 0; + } +} + +} // namespace WTF diff --git a/Source/JavaScriptCore/wtf/text/StringBuilder.h b/Source/JavaScriptCore/wtf/text/StringBuilder.h new file mode 100644 index 000000000..da1e8320d --- /dev/null +++ b/Source/JavaScriptCore/wtf/text/StringBuilder.h @@ -0,0 +1,234 @@ +/* + * Copyright (C) 2009, 2010 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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 APPLE INC. ``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 APPLE INC. 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. + */ + +#ifndef StringBuilder_h +#define StringBuilder_h + +#include <wtf/Vector.h> +#include <wtf/text/WTFString.h> + +namespace WTF { + +class StringBuilder { +public: + StringBuilder() + : m_length(0) + , m_is8Bit(true) + , m_valid16BitShadowLength(0) + , m_bufferCharacters8(0) + { + } + + void append(const UChar*, unsigned); + void append(const LChar*, unsigned); + + ALWAYS_INLINE void append(const char* characters, unsigned length) { append(reinterpret_cast<const LChar*>(characters), length); } + + void append(const String& string) + { + if (!string.length()) + return; + + // If we're appending to an empty string, and there is not buffer + // (in case reserveCapacity has been called) then just retain the + // string. + if (!m_length && !m_buffer) { + m_string = string; + m_length = string.length(); + m_is8Bit = m_string.is8Bit(); + return; + } + + if (string.is8Bit()) + append(string.characters8(), string.length()); + else + append(string.characters16(), string.length()); + } + + void append(const char* characters) + { + if (characters) + append(characters, strlen(characters)); + } + + void append(UChar c) + { + if (m_buffer && !m_is8Bit && m_length < m_buffer->length() && m_string.isNull()) + m_bufferCharacters16[m_length++] = c; + else + append(&c, 1); + } + + void append(LChar c) + { + if (m_buffer && m_length < m_buffer->length() && m_string.isNull()) { + if (m_is8Bit) + m_bufferCharacters8[m_length++] = c; + else + m_bufferCharacters16[m_length++] = c; + } else + append(&c, 1); + } + + void append(char c) + { + if (m_buffer && m_length < m_buffer->length() && m_string.isNull()) { + if (m_is8Bit) + m_bufferCharacters8[m_length++] = (LChar)c; + else + m_bufferCharacters16[m_length++] = (LChar)c; + } + else + append(&c, 1); + } + + String toString() + { + if (m_string.isNull()) { + shrinkToFit(); + reifyString(); + } + return m_string; + } + + String toStringPreserveCapacity() + { + if (m_string.isNull()) + reifyString(); + return m_string; + } + + unsigned length() const + { + return m_length; + } + + bool isEmpty() const { return !length(); } + + void reserveCapacity(unsigned newCapacity); + + void resize(unsigned newSize); + + void shrinkToFit(); + + UChar operator[](unsigned i) const + { + ASSERT(i < m_length); + if (m_is8Bit) + return characters8()[i]; + return characters16()[i]; + } + + const LChar* characters8() const + { + ASSERT(m_is8Bit); + if (!m_length) + return 0; + if (!m_string.isNull()) + return m_string.characters8(); + ASSERT(m_buffer); + return m_buffer->characters8(); + } + + const UChar* characters16() const + { + ASSERT(!m_is8Bit); + if (!m_length) + return 0; + if (!m_string.isNull()) + return m_string.characters16(); + ASSERT(m_buffer); + return m_buffer->characters16(); + } + + const UChar* characters() const + { + if (!m_length) + return 0; + if (!m_string.isNull()) + return m_string.characters(); + ASSERT(m_buffer); + if (m_buffer->has16BitShadow() && m_valid16BitShadowLength < m_length) + m_buffer->upconvertCharacters(m_valid16BitShadowLength, m_length); + + m_valid16BitShadowLength = m_length; + + return m_buffer->characters(); + } + + void clear() + { + m_length = 0; + m_string = String(); + m_buffer = 0; + m_bufferCharacters8 = 0; + m_is8Bit = true; + m_valid16BitShadowLength = 0; + } + +private: + void allocateBuffer(const LChar* currentCharacters, unsigned requiredLength); + void allocateBuffer(const UChar* currentCharacters, unsigned requiredLength); + void allocateBufferUpConvert(const LChar* currentCharacters, unsigned requiredLength); + template <typename CharType> + void reallocateBuffer(unsigned requiredLength); + template <typename CharType> + ALWAYS_INLINE CharType* appendUninitialized(unsigned length); + template <typename CharType> + CharType* appendUninitializedSlow(unsigned length); + template <typename CharType> + ALWAYS_INLINE CharType * getBufferCharacters(); + void reifyString(); + + unsigned m_length; + String m_string; + RefPtr<StringImpl> m_buffer; + bool m_is8Bit; + mutable unsigned m_valid16BitShadowLength; + union { + LChar* m_bufferCharacters8; + UChar* m_bufferCharacters16; + }; +}; + +template <> +ALWAYS_INLINE LChar* StringBuilder::getBufferCharacters<LChar>() +{ + ASSERT(m_is8Bit); + return m_bufferCharacters8; +} + +template <> +ALWAYS_INLINE UChar* StringBuilder::getBufferCharacters<UChar>() +{ + ASSERT(!m_is8Bit); + return m_bufferCharacters16; +} + +} // namespace WTF + +using WTF::StringBuilder; + +#endif // StringBuilder_h diff --git a/Source/JavaScriptCore/wtf/text/StringConcatenate.h b/Source/JavaScriptCore/wtf/text/StringConcatenate.h new file mode 100644 index 000000000..08d67dcf0 --- /dev/null +++ b/Source/JavaScriptCore/wtf/text/StringConcatenate.h @@ -0,0 +1,964 @@ +/* + * Copyright (C) 2010 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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 APPLE INC. ``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 APPLE INC. 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. + */ + +#ifndef StringConcatenate_h +#define StringConcatenate_h + +#ifndef WTFString_h +#include "AtomicString.h" +#endif + +// This macro is helpful for testing how many intermediate Strings are created while evaluating an +// expression containing operator+. +#ifndef WTF_STRINGTYPEADAPTER_COPIED_WTF_STRING +#define WTF_STRINGTYPEADAPTER_COPIED_WTF_STRING() ((void)0) +#endif + +namespace WTF { + +template<typename StringType> +class StringTypeAdapter { +}; + +template<> +class StringTypeAdapter<char> { +public: + StringTypeAdapter<char>(char buffer) + : m_buffer(buffer) + { + } + + unsigned length() { return 1; } + + bool is8Bit() { return true; } + + void writeTo(LChar* destination) + { + *destination = m_buffer; + } + + void writeTo(UChar* destination) { *destination = m_buffer; } + +private: + unsigned char m_buffer; +}; + +template<> +class StringTypeAdapter<LChar> { +public: + StringTypeAdapter<LChar>(LChar buffer) + : m_buffer(buffer) + { + } + + unsigned length() { return 1; } + + bool is8Bit() { return true; } + + void writeTo(LChar* destination) + { + *destination = m_buffer; + } + + void writeTo(UChar* destination) { *destination = m_buffer; } + +private: + LChar m_buffer; +}; + +template<> +class StringTypeAdapter<UChar> { +public: + StringTypeAdapter<UChar>(UChar buffer) + : m_buffer(buffer) + { + } + + unsigned length() { return 1; } + + bool is8Bit() { return m_buffer <= 0xff; } + + void writeTo(LChar* destination) + { + ASSERT(is8Bit()); + *destination = static_cast<LChar>(m_buffer); + } + + void writeTo(UChar* destination) { *destination = m_buffer; } + +private: + UChar m_buffer; +}; + +template<> +class StringTypeAdapter<char*> { +public: + StringTypeAdapter<char*>(char* buffer) + : m_buffer(buffer) + , m_length(strlen(buffer)) + { + } + + unsigned length() { return m_length; } + + bool is8Bit() { return true; } + + void writeTo(LChar* destination) + { + for (unsigned i = 0; i < m_length; ++i) + destination[i] = static_cast<LChar>(m_buffer[i]); + } + + void writeTo(UChar* destination) + { + for (unsigned i = 0; i < m_length; ++i) { + unsigned char c = m_buffer[i]; + destination[i] = c; + } + } + +private: + const char* m_buffer; + unsigned m_length; +}; + +template<> +class StringTypeAdapter<LChar*> { +public: + StringTypeAdapter<LChar*>(LChar* buffer) + : m_buffer(buffer) + , m_length(strlen(reinterpret_cast<char*>(buffer))) + { + } + + unsigned length() { return m_length; } + + bool is8Bit() { return true; } + + void writeTo(LChar* destination) + { + memcpy(destination, m_buffer, m_length * sizeof(LChar)); + } + + void writeTo(UChar* destination) + { + for (unsigned i = 0; i < m_length; ++i) + destination[i] = m_buffer[i]; + } + +private: + const LChar* m_buffer; + unsigned m_length; +}; + +template<> +class StringTypeAdapter<const UChar*> { +public: + StringTypeAdapter<const UChar*>(const UChar* buffer) + : m_buffer(buffer) + { + size_t len = 0; + while (m_buffer[len] != UChar(0)) + len++; + + if (len > std::numeric_limits<unsigned>::max()) + CRASH(); + + m_length = len; + } + + unsigned length() { return m_length; } + + bool is8Bit() { return false; } + + NO_RETURN_DUE_TO_CRASH void writeTo(LChar*) + { + CRASH(); + } + + void writeTo(UChar* destination) + { + memcpy(destination, m_buffer, m_length * sizeof(UChar)); + } + +private: + const UChar* m_buffer; + unsigned m_length; +}; + +template<> +class StringTypeAdapter<const char*> { +public: + StringTypeAdapter<const char*>(const char* buffer) + : m_buffer(buffer) + , m_length(strlen(buffer)) + { + } + + unsigned length() { return m_length; } + + bool is8Bit() { return true; } + + void writeTo(LChar* destination) + { + memcpy(destination, m_buffer, static_cast<size_t>(m_length) * sizeof(LChar)); + } + + void writeTo(UChar* destination) + { + for (unsigned i = 0; i < m_length; ++i) { + unsigned char c = m_buffer[i]; + destination[i] = c; + } + } + +private: + const char* m_buffer; + unsigned m_length; +}; + +template<> +class StringTypeAdapter<const LChar*> { +public: + StringTypeAdapter<const LChar*>(const LChar* buffer) + : m_buffer(buffer) + , m_length(strlen(reinterpret_cast<const char*>(buffer))) + { + } + + unsigned length() { return m_length; } + + bool is8Bit() { return true; } + + void writeTo(LChar* destination) + { + memcpy(destination, m_buffer, static_cast<size_t>(m_length) * sizeof(LChar)); + } + + void writeTo(UChar* destination) + { + for (unsigned i = 0; i < m_length; ++i) + destination[i] = m_buffer[i]; + } + +private: + const LChar* m_buffer; + unsigned m_length; +}; + +template<> +class StringTypeAdapter<Vector<char> > { +public: + StringTypeAdapter<Vector<char> >(const Vector<char>& buffer) + : m_buffer(buffer) + { + } + + size_t length() { return m_buffer.size(); } + + bool is8Bit() { return true; } + + void writeTo(LChar* destination) + { + for (size_t i = 0; i < m_buffer.size(); ++i) + destination[i] = static_cast<unsigned char>(m_buffer[i]); + } + + void writeTo(UChar* destination) + { + for (size_t i = 0; i < m_buffer.size(); ++i) + destination[i] = static_cast<unsigned char>(m_buffer[i]); + } + +private: + const Vector<char>& m_buffer; +}; + +template<> +class StringTypeAdapter<Vector<LChar> > { +public: + StringTypeAdapter<Vector<LChar> >(const Vector<LChar>& buffer) + : m_buffer(buffer) + { + } + + size_t length() { return m_buffer.size(); } + + bool is8Bit() { return true; } + + void writeTo(LChar* destination) + { + for (size_t i = 0; i < m_buffer.size(); ++i) + destination[i] = m_buffer[i]; + } + + void writeTo(UChar* destination) + { + for (size_t i = 0; i < m_buffer.size(); ++i) + destination[i] = m_buffer[i]; + } + +private: + const Vector<LChar>& m_buffer; +}; + +template<> +class StringTypeAdapter<String> { +public: + StringTypeAdapter<String>(const String& string) + : m_buffer(string) + { + } + + unsigned length() { return m_buffer.length(); } + + bool is8Bit() { return m_buffer.isNull() || m_buffer.is8Bit(); } + + void writeTo(LChar* destination) + { + unsigned length = m_buffer.length(); + + ASSERT(is8Bit()); + const LChar* data = m_buffer.characters8(); + for (unsigned i = 0; i < length; ++i) + destination[i] = data[i]; + + WTF_STRINGTYPEADAPTER_COPIED_WTF_STRING(); + } + + void writeTo(UChar* destination) + { + unsigned length = m_buffer.length(); + + if (is8Bit()) { + const LChar* data = m_buffer.characters8(); + for (unsigned i = 0; i < length; ++i) + destination[i] = data[i]; + } else { + const UChar* data = m_buffer.characters16(); + for (unsigned i = 0; i < length; ++i) + destination[i] = data[i]; + } + + WTF_STRINGTYPEADAPTER_COPIED_WTF_STRING(); + } + +private: + const String& m_buffer; +}; + +template<> +class StringTypeAdapter<AtomicString> { +public: + StringTypeAdapter<AtomicString>(const AtomicString& string) + : m_adapter(string.string()) + { + } + + unsigned length() { return m_adapter.length(); } + + bool is8Bit() { return m_adapter.is8Bit(); } + + void writeTo(LChar* destination) { m_adapter.writeTo(destination); } + void writeTo(UChar* destination) { m_adapter.writeTo(destination); } + +private: + StringTypeAdapter<String> m_adapter; +}; + +inline void sumWithOverflow(unsigned& total, unsigned addend, bool& overflow) +{ + unsigned oldTotal = total; + total = oldTotal + addend; + if (total < oldTotal) + overflow = true; +} + +template<typename StringType1, typename StringType2> +PassRefPtr<StringImpl> tryMakeString(StringType1 string1, StringType2 string2) +{ + StringTypeAdapter<StringType1> adapter1(string1); + StringTypeAdapter<StringType2> adapter2(string2); + + bool overflow = false; + unsigned length = adapter1.length(); + sumWithOverflow(length, adapter2.length(), overflow); + if (overflow) + return 0; + + if (adapter1.is8Bit() && adapter2.is8Bit()) { + LChar* buffer; + RefPtr<StringImpl> resultImpl = StringImpl::tryCreateUninitialized(length, buffer); + if (!resultImpl) + return 0; + + LChar* result = buffer; + adapter1.writeTo(result); + result += adapter1.length(); + adapter2.writeTo(result); + + return resultImpl.release(); + } + + UChar* buffer; + RefPtr<StringImpl> resultImpl = StringImpl::tryCreateUninitialized(length, buffer); + if (!resultImpl) + return 0; + + UChar* result = buffer; + adapter1.writeTo(result); + result += adapter1.length(); + adapter2.writeTo(result); + + return resultImpl.release(); +} + +template<typename StringType1, typename StringType2, typename StringType3> +PassRefPtr<StringImpl> tryMakeString(StringType1 string1, StringType2 string2, StringType3 string3) +{ + StringTypeAdapter<StringType1> adapter1(string1); + StringTypeAdapter<StringType2> adapter2(string2); + StringTypeAdapter<StringType3> adapter3(string3); + + bool overflow = false; + unsigned length = adapter1.length(); + sumWithOverflow(length, adapter2.length(), overflow); + sumWithOverflow(length, adapter3.length(), overflow); + if (overflow) + return 0; + + if (adapter1.is8Bit() && adapter2.is8Bit() && adapter3.is8Bit()) { + LChar* buffer; + RefPtr<StringImpl> resultImpl = StringImpl::tryCreateUninitialized(length, buffer); + if (!resultImpl) + return 0; + + LChar* result = buffer; + adapter1.writeTo(result); + result += adapter1.length(); + adapter2.writeTo(result); + result += adapter2.length(); + adapter3.writeTo(result); + + return resultImpl.release(); + } + + UChar* buffer = 0; + RefPtr<StringImpl> resultImpl = StringImpl::tryCreateUninitialized(length, buffer); + if (!resultImpl) + return 0; + + UChar* result = buffer; + adapter1.writeTo(result); + result += adapter1.length(); + adapter2.writeTo(result); + result += adapter2.length(); + adapter3.writeTo(result); + + return resultImpl.release(); +} + +template<typename StringType1, typename StringType2, typename StringType3, typename StringType4> +PassRefPtr<StringImpl> tryMakeString(StringType1 string1, StringType2 string2, StringType3 string3, StringType4 string4) +{ + StringTypeAdapter<StringType1> adapter1(string1); + StringTypeAdapter<StringType2> adapter2(string2); + StringTypeAdapter<StringType3> adapter3(string3); + StringTypeAdapter<StringType4> adapter4(string4); + + bool overflow = false; + unsigned length = adapter1.length(); + sumWithOverflow(length, adapter2.length(), overflow); + sumWithOverflow(length, adapter3.length(), overflow); + sumWithOverflow(length, adapter4.length(), overflow); + if (overflow) + return 0; + + if (adapter1.is8Bit() && adapter2.is8Bit() && adapter3.is8Bit() && adapter4.is8Bit()) { + LChar* buffer; + RefPtr<StringImpl> resultImpl = StringImpl::tryCreateUninitialized(length, buffer); + if (!resultImpl) + return 0; + + LChar* result = buffer; + adapter1.writeTo(result); + result += adapter1.length(); + adapter2.writeTo(result); + result += adapter2.length(); + adapter3.writeTo(result); + result += adapter3.length(); + adapter4.writeTo(result); + + return resultImpl.release(); + } + + UChar* buffer; + RefPtr<StringImpl> resultImpl = StringImpl::tryCreateUninitialized(length, buffer); + if (!resultImpl) + return 0; + + UChar* result = buffer; + adapter1.writeTo(result); + result += adapter1.length(); + adapter2.writeTo(result); + result += adapter2.length(); + adapter3.writeTo(result); + result += adapter3.length(); + adapter4.writeTo(result); + + return resultImpl.release(); +} + +template<typename StringType1, typename StringType2, typename StringType3, typename StringType4, typename StringType5> +PassRefPtr<StringImpl> tryMakeString(StringType1 string1, StringType2 string2, StringType3 string3, StringType4 string4, StringType5 string5) +{ + StringTypeAdapter<StringType1> adapter1(string1); + StringTypeAdapter<StringType2> adapter2(string2); + StringTypeAdapter<StringType3> adapter3(string3); + StringTypeAdapter<StringType4> adapter4(string4); + StringTypeAdapter<StringType5> adapter5(string5); + + bool overflow = false; + unsigned length = adapter1.length(); + sumWithOverflow(length, adapter2.length(), overflow); + sumWithOverflow(length, adapter3.length(), overflow); + sumWithOverflow(length, adapter4.length(), overflow); + sumWithOverflow(length, adapter5.length(), overflow); + if (overflow) + return 0; + + if (adapter1.is8Bit() && adapter2.is8Bit() && adapter3.is8Bit() && adapter4.is8Bit() && adapter5.is8Bit()) { + LChar* buffer; + RefPtr<StringImpl> resultImpl = StringImpl::tryCreateUninitialized(length, buffer); + if (!resultImpl) + return 0; + + LChar* result = buffer; + adapter1.writeTo(result); + result += adapter1.length(); + adapter2.writeTo(result); + result += adapter2.length(); + adapter3.writeTo(result); + result += adapter3.length(); + adapter4.writeTo(result); + result += adapter4.length(); + adapter5.writeTo(result); + + return resultImpl.release(); + } + + UChar* buffer; + RefPtr<StringImpl> resultImpl = StringImpl::tryCreateUninitialized(length, buffer); + if (!resultImpl) + return 0; + + UChar* result = buffer; + adapter1.writeTo(result); + result += adapter1.length(); + adapter2.writeTo(result); + result += adapter2.length(); + adapter3.writeTo(result); + result += adapter3.length(); + adapter4.writeTo(result); + result += adapter4.length(); + adapter5.writeTo(result); + + return resultImpl.release(); +} + +template<typename StringType1, typename StringType2, typename StringType3, typename StringType4, typename StringType5, typename StringType6> +PassRefPtr<StringImpl> tryMakeString(StringType1 string1, StringType2 string2, StringType3 string3, StringType4 string4, StringType5 string5, StringType6 string6) +{ + StringTypeAdapter<StringType1> adapter1(string1); + StringTypeAdapter<StringType2> adapter2(string2); + StringTypeAdapter<StringType3> adapter3(string3); + StringTypeAdapter<StringType4> adapter4(string4); + StringTypeAdapter<StringType5> adapter5(string5); + StringTypeAdapter<StringType6> adapter6(string6); + + bool overflow = false; + unsigned length = adapter1.length(); + sumWithOverflow(length, adapter2.length(), overflow); + sumWithOverflow(length, adapter3.length(), overflow); + sumWithOverflow(length, adapter4.length(), overflow); + sumWithOverflow(length, adapter5.length(), overflow); + sumWithOverflow(length, adapter6.length(), overflow); + if (overflow) + return 0; + + if (adapter1.is8Bit() && adapter2.is8Bit() && adapter3.is8Bit() && adapter4.is8Bit() && adapter5.is8Bit() && adapter6.is8Bit()) { + LChar* buffer; + RefPtr<StringImpl> resultImpl = StringImpl::tryCreateUninitialized(length, buffer); + if (!resultImpl) + return 0; + + LChar* result = buffer; + adapter1.writeTo(result); + result += adapter1.length(); + adapter2.writeTo(result); + result += adapter2.length(); + adapter3.writeTo(result); + result += adapter3.length(); + adapter4.writeTo(result); + result += adapter4.length(); + adapter5.writeTo(result); + result += adapter5.length(); + adapter6.writeTo(result); + + return resultImpl.release(); + } + + UChar* buffer; + RefPtr<StringImpl> resultImpl = StringImpl::tryCreateUninitialized(length, buffer); + if (!resultImpl) + return 0; + + UChar* result = buffer; + adapter1.writeTo(result); + result += adapter1.length(); + adapter2.writeTo(result); + result += adapter2.length(); + adapter3.writeTo(result); + result += adapter3.length(); + adapter4.writeTo(result); + result += adapter4.length(); + adapter5.writeTo(result); + result += adapter5.length(); + adapter6.writeTo(result); + + return resultImpl.release(); +} + +template<typename StringType1, typename StringType2, typename StringType3, typename StringType4, typename StringType5, typename StringType6, typename StringType7> +PassRefPtr<StringImpl> tryMakeString(StringType1 string1, StringType2 string2, StringType3 string3, StringType4 string4, StringType5 string5, StringType6 string6, StringType7 string7) +{ + StringTypeAdapter<StringType1> adapter1(string1); + StringTypeAdapter<StringType2> adapter2(string2); + StringTypeAdapter<StringType3> adapter3(string3); + StringTypeAdapter<StringType4> adapter4(string4); + StringTypeAdapter<StringType5> adapter5(string5); + StringTypeAdapter<StringType6> adapter6(string6); + StringTypeAdapter<StringType7> adapter7(string7); + + bool overflow = false; + unsigned length = adapter1.length(); + sumWithOverflow(length, adapter2.length(), overflow); + sumWithOverflow(length, adapter3.length(), overflow); + sumWithOverflow(length, adapter4.length(), overflow); + sumWithOverflow(length, adapter5.length(), overflow); + sumWithOverflow(length, adapter6.length(), overflow); + sumWithOverflow(length, adapter7.length(), overflow); + if (overflow) + return 0; + + if (adapter1.is8Bit() && adapter2.is8Bit() && adapter3.is8Bit() && adapter4.is8Bit() && adapter5.is8Bit() && adapter6.is8Bit()) { + LChar* buffer; + RefPtr<StringImpl> resultImpl = StringImpl::tryCreateUninitialized(length, buffer); + if (!resultImpl) + return 0; + + LChar* result = buffer; + adapter1.writeTo(result); + result += adapter1.length(); + adapter2.writeTo(result); + result += adapter2.length(); + adapter3.writeTo(result); + result += adapter3.length(); + adapter4.writeTo(result); + result += adapter4.length(); + adapter5.writeTo(result); + result += adapter5.length(); + adapter6.writeTo(result); + result += adapter6.length(); + adapter7.writeTo(result); + + return resultImpl.release(); + } + + UChar* buffer; + RefPtr<StringImpl> resultImpl = StringImpl::tryCreateUninitialized(length, buffer); + if (!resultImpl) + return 0; + + UChar* result = buffer; + adapter1.writeTo(result); + result += adapter1.length(); + adapter2.writeTo(result); + result += adapter2.length(); + adapter3.writeTo(result); + result += adapter3.length(); + adapter4.writeTo(result); + result += adapter4.length(); + adapter5.writeTo(result); + result += adapter5.length(); + adapter6.writeTo(result); + result += adapter6.length(); + adapter7.writeTo(result); + + return resultImpl.release(); +} + +template<typename StringType1, typename StringType2, typename StringType3, typename StringType4, typename StringType5, typename StringType6, typename StringType7, typename StringType8> +PassRefPtr<StringImpl> tryMakeString(StringType1 string1, StringType2 string2, StringType3 string3, StringType4 string4, StringType5 string5, StringType6 string6, StringType7 string7, StringType8 string8) +{ + StringTypeAdapter<StringType1> adapter1(string1); + StringTypeAdapter<StringType2> adapter2(string2); + StringTypeAdapter<StringType3> adapter3(string3); + StringTypeAdapter<StringType4> adapter4(string4); + StringTypeAdapter<StringType5> adapter5(string5); + StringTypeAdapter<StringType6> adapter6(string6); + StringTypeAdapter<StringType7> adapter7(string7); + StringTypeAdapter<StringType8> adapter8(string8); + + bool overflow = false; + unsigned length = adapter1.length(); + sumWithOverflow(length, adapter2.length(), overflow); + sumWithOverflow(length, adapter3.length(), overflow); + sumWithOverflow(length, adapter4.length(), overflow); + sumWithOverflow(length, adapter5.length(), overflow); + sumWithOverflow(length, adapter6.length(), overflow); + sumWithOverflow(length, adapter7.length(), overflow); + sumWithOverflow(length, adapter8.length(), overflow); + if (overflow) + return 0; + + if (adapter1.is8Bit() && adapter2.is8Bit() && adapter3.is8Bit() && adapter4.is8Bit() && adapter5.is8Bit() && adapter6.is8Bit() && adapter7.is8Bit() && adapter8.is8Bit()) { + LChar* buffer; + RefPtr<StringImpl> resultImpl = StringImpl::tryCreateUninitialized(length, buffer); + if (!resultImpl) + return 0; + + LChar* result = buffer; + adapter1.writeTo(result); + result += adapter1.length(); + adapter2.writeTo(result); + result += adapter2.length(); + adapter3.writeTo(result); + result += adapter3.length(); + adapter4.writeTo(result); + result += adapter4.length(); + adapter5.writeTo(result); + result += adapter5.length(); + adapter6.writeTo(result); + result += adapter6.length(); + adapter7.writeTo(result); + result += adapter7.length(); + adapter8.writeTo(result); + + return resultImpl.release(); + } + + UChar* buffer; + RefPtr<StringImpl> resultImpl = StringImpl::tryCreateUninitialized(length, buffer); + if (!resultImpl) + return 0; + + UChar* result = buffer; + adapter1.writeTo(result); + result += adapter1.length(); + adapter2.writeTo(result); + result += adapter2.length(); + adapter3.writeTo(result); + result += adapter3.length(); + adapter4.writeTo(result); + result += adapter4.length(); + adapter5.writeTo(result); + result += adapter5.length(); + adapter6.writeTo(result); + result += adapter6.length(); + adapter7.writeTo(result); + result += adapter7.length(); + adapter8.writeTo(result); + + return resultImpl.release(); +} + +template<typename StringType1, typename StringType2, typename StringType3, typename StringType4, typename StringType5, typename StringType6, typename StringType7, typename StringType8, typename StringType9> +PassRefPtr<StringImpl> tryMakeString(StringType1 string1, StringType2 string2, StringType3 string3, StringType4 string4, StringType5 string5, StringType6 string6, StringType7 string7, StringType8 string8, StringType9 string9) +{ + StringTypeAdapter<StringType1> adapter1(string1); + StringTypeAdapter<StringType2> adapter2(string2); + StringTypeAdapter<StringType3> adapter3(string3); + StringTypeAdapter<StringType4> adapter4(string4); + StringTypeAdapter<StringType5> adapter5(string5); + StringTypeAdapter<StringType6> adapter6(string6); + StringTypeAdapter<StringType7> adapter7(string7); + StringTypeAdapter<StringType8> adapter8(string8); + StringTypeAdapter<StringType9> adapter9(string9); + + bool overflow = false; + unsigned length = adapter1.length(); + sumWithOverflow(length, adapter2.length(), overflow); + sumWithOverflow(length, adapter3.length(), overflow); + sumWithOverflow(length, adapter4.length(), overflow); + sumWithOverflow(length, adapter5.length(), overflow); + sumWithOverflow(length, adapter6.length(), overflow); + sumWithOverflow(length, adapter7.length(), overflow); + sumWithOverflow(length, adapter8.length(), overflow); + sumWithOverflow(length, adapter9.length(), overflow); + if (overflow) + return 0; + + if (adapter1.is8Bit() && adapter2.is8Bit() && adapter3.is8Bit() && adapter4.is8Bit() && adapter5.is8Bit() && adapter6.is8Bit() && adapter7.is8Bit() && adapter8.is8Bit() && adapter9.is8Bit()) { + LChar* buffer; + RefPtr<StringImpl> resultImpl = StringImpl::tryCreateUninitialized(length, buffer); + if (!resultImpl) + return 0; + + LChar* result = buffer; + adapter1.writeTo(result); + result += adapter1.length(); + adapter2.writeTo(result); + result += adapter2.length(); + adapter3.writeTo(result); + result += adapter3.length(); + adapter4.writeTo(result); + result += adapter4.length(); + adapter5.writeTo(result); + result += adapter5.length(); + adapter6.writeTo(result); + result += adapter6.length(); + adapter7.writeTo(result); + result += adapter7.length(); + adapter8.writeTo(result); + result += adapter8.length(); + adapter9.writeTo(result); + + return resultImpl.release(); + } + + UChar* buffer; + RefPtr<StringImpl> resultImpl = StringImpl::tryCreateUninitialized(length, buffer); + if (!resultImpl) + return 0; + + UChar* result = buffer; + adapter1.writeTo(result); + result += adapter1.length(); + adapter2.writeTo(result); + result += adapter2.length(); + adapter3.writeTo(result); + result += adapter3.length(); + adapter4.writeTo(result); + result += adapter4.length(); + adapter5.writeTo(result); + result += adapter5.length(); + adapter6.writeTo(result); + result += adapter6.length(); + adapter7.writeTo(result); + result += adapter7.length(); + adapter8.writeTo(result); + result += adapter8.length(); + adapter9.writeTo(result); + + return resultImpl.release(); +} + + +// Convenience only. +template<typename StringType1> +String makeString(StringType1 string1) +{ + return String(string1); +} + +template<typename StringType1, typename StringType2> +String makeString(StringType1 string1, StringType2 string2) +{ + RefPtr<StringImpl> resultImpl = tryMakeString(string1, string2); + if (!resultImpl) + CRASH(); + return resultImpl.release(); +} + +template<typename StringType1, typename StringType2, typename StringType3> +String makeString(StringType1 string1, StringType2 string2, StringType3 string3) +{ + RefPtr<StringImpl> resultImpl = tryMakeString(string1, string2, string3); + if (!resultImpl) + CRASH(); + return resultImpl.release(); +} + +template<typename StringType1, typename StringType2, typename StringType3, typename StringType4> +String makeString(StringType1 string1, StringType2 string2, StringType3 string3, StringType4 string4) +{ + RefPtr<StringImpl> resultImpl = tryMakeString(string1, string2, string3, string4); + if (!resultImpl) + CRASH(); + return resultImpl.release(); +} + +template<typename StringType1, typename StringType2, typename StringType3, typename StringType4, typename StringType5> +String makeString(StringType1 string1, StringType2 string2, StringType3 string3, StringType4 string4, StringType5 string5) +{ + RefPtr<StringImpl> resultImpl = tryMakeString(string1, string2, string3, string4, string5); + if (!resultImpl) + CRASH(); + return resultImpl.release(); +} + +template<typename StringType1, typename StringType2, typename StringType3, typename StringType4, typename StringType5, typename StringType6> +String makeString(StringType1 string1, StringType2 string2, StringType3 string3, StringType4 string4, StringType5 string5, StringType6 string6) +{ + RefPtr<StringImpl> resultImpl = tryMakeString(string1, string2, string3, string4, string5, string6); + if (!resultImpl) + CRASH(); + return resultImpl.release(); +} + +template<typename StringType1, typename StringType2, typename StringType3, typename StringType4, typename StringType5, typename StringType6, typename StringType7> +String makeString(StringType1 string1, StringType2 string2, StringType3 string3, StringType4 string4, StringType5 string5, StringType6 string6, StringType7 string7) +{ + RefPtr<StringImpl> resultImpl = tryMakeString(string1, string2, string3, string4, string5, string6, string7); + if (!resultImpl) + CRASH(); + return resultImpl.release(); +} + +template<typename StringType1, typename StringType2, typename StringType3, typename StringType4, typename StringType5, typename StringType6, typename StringType7, typename StringType8> +String makeString(StringType1 string1, StringType2 string2, StringType3 string3, StringType4 string4, StringType5 string5, StringType6 string6, StringType7 string7, StringType8 string8) +{ + RefPtr<StringImpl> resultImpl = tryMakeString(string1, string2, string3, string4, string5, string6, string7, string8); + if (!resultImpl) + CRASH(); + return resultImpl.release(); +} + +template<typename StringType1, typename StringType2, typename StringType3, typename StringType4, typename StringType5, typename StringType6, typename StringType7, typename StringType8, typename StringType9> +String makeString(StringType1 string1, StringType2 string2, StringType3 string3, StringType4 string4, StringType5 string5, StringType6 string6, StringType7 string7, StringType8 string8, StringType9 string9) +{ + RefPtr<StringImpl> resultImpl = tryMakeString(string1, string2, string3, string4, string5, string6, string7, string8, string9); + if (!resultImpl) + CRASH(); + return resultImpl.release(); +} + +} // namespace WTF + +using WTF::makeString; + +#include "StringOperators.h" +#endif diff --git a/Source/JavaScriptCore/wtf/text/StringHash.h b/Source/JavaScriptCore/wtf/text/StringHash.h new file mode 100644 index 000000000..cde591c18 --- /dev/null +++ b/Source/JavaScriptCore/wtf/text/StringHash.h @@ -0,0 +1,184 @@ +/* + * Copyright (C) 2006, 2007, 2008 Apple Inc. All rights reserved + * Copyright (C) Research In Motion Limited 2009. All rights reserved. + * + * This library 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 library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#ifndef StringHash_h +#define StringHash_h + +#include "AtomicString.h" +#include "WTFString.h" +#include <wtf/Forward.h> +#include <wtf/HashTraits.h> +#include <wtf/StringHasher.h> +#include <wtf/unicode/Unicode.h> + +namespace WTF { + + // The hash() functions on StringHash and CaseFoldingHash do not support + // null strings. get(), contains(), and add() on HashMap<String,..., StringHash> + // cause a null-pointer dereference when passed null strings. + + // FIXME: We should really figure out a way to put the computeHash function that's + // currently a member function of StringImpl into this file so we can be a little + // closer to having all the nearly-identical hash functions in one place. + + struct StringHash { + static unsigned hash(StringImpl* key) { return key->hash(); } + static bool equal(const StringImpl* a, const StringImpl* b) + { + if (a == b) + return true; + if (!a || !b) + return false; + + unsigned aLength = a->length(); + unsigned bLength = b->length(); + if (aLength != bLength) + return false; + + if (a->is8Bit()) { + if (b->is8Bit()) { + // Both a & b are 8 bit. + return WTF::equal(a->characters8(), b->characters8(), aLength); + } + + // We know that a is 8 bit & b is 16 bit. + return WTF::equal(a->characters8(), b->characters16(), aLength); + } + + if (b->is8Bit()) { + // We know that a is 8 bit and b is 16 bit. + return WTF::equal(a->characters16(), b->characters8(), aLength); + } + + return WTF::equal(a->characters16(), b->characters16(), aLength); + } + + static unsigned hash(const RefPtr<StringImpl>& key) { return key->hash(); } + static bool equal(const RefPtr<StringImpl>& a, const RefPtr<StringImpl>& b) + { + return equal(a.get(), b.get()); + } + + static unsigned hash(const String& key) { return key.impl()->hash(); } + static bool equal(const String& a, const String& b) + { + return equal(a.impl(), b.impl()); + } + + static const bool safeToCompareToEmptyOrDeleted = false; + }; + + class CaseFoldingHash { + public: + template<typename T> static inline UChar foldCase(T ch) + { + return WTF::Unicode::foldCase(ch); + } + + static unsigned hash(const UChar* data, unsigned length) + { + return StringHasher::computeHash<UChar, foldCase<UChar> >(data, length); + } + + static unsigned hash(StringImpl* str) + { + return hash(str->characters(), str->length()); + } + + static unsigned hash(const LChar* data, unsigned length) + { + return StringHasher::computeHash<LChar, foldCase<LChar> >(data, length); + } + + static inline unsigned hash(const char* data, unsigned length) + { + return CaseFoldingHash::hash(reinterpret_cast<const LChar*>(data), length); + } + + static bool equal(const StringImpl* a, const StringImpl* b) + { + if (a == b) + return true; + if (!a || !b) + return false; + unsigned length = a->length(); + if (length != b->length()) + return false; + return WTF::Unicode::umemcasecmp(a->characters(), b->characters(), length) == 0; + } + + static unsigned hash(const RefPtr<StringImpl>& key) + { + return hash(key.get()); + } + + static bool equal(const RefPtr<StringImpl>& a, const RefPtr<StringImpl>& b) + { + return equal(a.get(), b.get()); + } + + static unsigned hash(const String& key) + { + return hash(key.impl()); + } + static unsigned hash(const AtomicString& key) + { + return hash(key.impl()); + } + static bool equal(const String& a, const String& b) + { + return equal(a.impl(), b.impl()); + } + static bool equal(const AtomicString& a, const AtomicString& b) + { + return (a == b) || equal(a.impl(), b.impl()); + } + + static const bool safeToCompareToEmptyOrDeleted = false; + }; + + // This hash can be used in cases where the key is a hash of a string, but we don't + // want to store the string. It's not really specific to string hashing, but all our + // current uses of it are for strings. + struct AlreadyHashed : IntHash<unsigned> { + static unsigned hash(unsigned key) { return key; } + + // To use a hash value as a key for a hash table, we need to eliminate the + // "deleted" value, which is negative one. That could be done by changing + // the string hash function to never generate negative one, but this works + // and is still relatively efficient. + static unsigned avoidDeletedValue(unsigned hash) + { + ASSERT(hash); + unsigned newHash = hash | (!(hash + 1) << 31); + ASSERT(newHash); + ASSERT(newHash != 0xFFFFFFFF); + return newHash; + } + }; + +} + +using WTF::StringHash; +using WTF::CaseFoldingHash; +using WTF::AlreadyHashed; + +#endif diff --git a/Source/JavaScriptCore/wtf/text/StringImpl.cpp b/Source/JavaScriptCore/wtf/text/StringImpl.cpp new file mode 100644 index 000000000..aa5a8d56e --- /dev/null +++ b/Source/JavaScriptCore/wtf/text/StringImpl.cpp @@ -0,0 +1,1541 @@ +/* + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * (C) 2001 Dirk Mueller ( mueller@kde.org ) + * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009 Apple Inc. All rights reserved. + * Copyright (C) 2006 Andrew Wellington (proton@wiretapped.net) + * + * This library 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 library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#include "config.h" +#include "StringImpl.h" + +#include "AtomicString.h" +#include "StringBuffer.h" +#include "StringHash.h" +#include <wtf/StdLibExtras.h> +#include <wtf/WTFThreadData.h> + +using namespace std; + +namespace WTF { + +using namespace Unicode; + +COMPILE_ASSERT(sizeof(StringImpl) == 2 * sizeof(int) + 3 * sizeof(void*), StringImpl_should_stay_small); + +StringImpl::~StringImpl() +{ + ASSERT(!isStatic()); + + if (isAtomic()) + AtomicString::remove(this); +#if USE(JSC) + if (isIdentifier()) { + if (!wtfThreadData().currentIdentifierTable()->remove(this)) + CRASH(); + } +#endif + + BufferOwnership ownership = bufferOwnership(); + + if (has16BitShadow()) { + ASSERT(m_copyData16); + fastFree(m_copyData16); + } + + if (ownership == BufferInternal) + return; + if (ownership == BufferOwned) { + // We use m_data8, but since it is a union with m_data16 this works either way. + ASSERT(m_data8); + fastFree(const_cast<LChar*>(m_data8)); + return; + } + + ASSERT(ownership == BufferSubstring); + ASSERT(m_substringBuffer); + m_substringBuffer->deref(); +} + +PassRefPtr<StringImpl> StringImpl::createUninitialized(unsigned length, LChar*& data) +{ + if (!length) { + data = 0; + return empty(); + } + + // Allocate a single buffer large enough to contain the StringImpl + // struct as well as the data which it contains. This removes one + // heap allocation from this call. + if (length > ((std::numeric_limits<unsigned>::max() - sizeof(StringImpl)) / sizeof(LChar))) + CRASH(); + size_t size = sizeof(StringImpl) + length * sizeof(LChar); + StringImpl* string = static_cast<StringImpl*>(fastMalloc(size)); + + data = reinterpret_cast<LChar*>(string + 1); + return adoptRef(new (NotNull, string) StringImpl(length, Force8BitConstructor)); +} + +PassRefPtr<StringImpl> StringImpl::createUninitialized(unsigned length, UChar*& data) +{ + if (!length) { + data = 0; + return empty(); + } + + // Allocate a single buffer large enough to contain the StringImpl + // struct as well as the data which it contains. This removes one + // heap allocation from this call. + if (length > ((std::numeric_limits<unsigned>::max() - sizeof(StringImpl)) / sizeof(UChar))) + CRASH(); + size_t size = sizeof(StringImpl) + length * sizeof(UChar); + StringImpl* string = static_cast<StringImpl*>(fastMalloc(size)); + + data = reinterpret_cast<UChar*>(string + 1); + return adoptRef(new (NotNull, string) StringImpl(length)); +} + +PassRefPtr<StringImpl> StringImpl::reallocate(PassRefPtr<StringImpl> originalString, unsigned length, LChar*& data) +{ + ASSERT(originalString->is8Bit()); + ASSERT(originalString->hasOneRef()); + ASSERT(originalString->bufferOwnership() == BufferInternal); + + if (!length) { + data = 0; + return empty(); + } + + // Same as createUninitialized() except here we use fastRealloc. + if (length > ((std::numeric_limits<unsigned>::max() - sizeof(StringImpl)) / sizeof(LChar))) + CRASH(); + size_t size = sizeof(StringImpl) + length * sizeof(LChar); + originalString->~StringImpl(); + StringImpl* string = static_cast<StringImpl*>(fastRealloc(originalString.leakRef(), size)); + + data = reinterpret_cast<LChar*>(string + 1); + return adoptRef(new (NotNull, string) StringImpl(length, Force8BitConstructor)); +} + +PassRefPtr<StringImpl> StringImpl::reallocate(PassRefPtr<StringImpl> originalString, unsigned length, UChar*& data) +{ + ASSERT(!originalString->is8Bit()); + ASSERT(originalString->hasOneRef()); + ASSERT(originalString->bufferOwnership() == BufferInternal); + + if (!length) { + data = 0; + return empty(); + } + + // Same as createUninitialized() except here we use fastRealloc. + if (length > ((std::numeric_limits<unsigned>::max() - sizeof(StringImpl)) / sizeof(UChar))) + CRASH(); + size_t size = sizeof(StringImpl) + length * sizeof(UChar); + originalString->~StringImpl(); + StringImpl* string = static_cast<StringImpl*>(fastRealloc(originalString.leakRef(), size)); + + data = reinterpret_cast<UChar*>(string + 1); + return adoptRef(new (NotNull, string) StringImpl(length)); +} + +PassRefPtr<StringImpl> StringImpl::create(const UChar* characters, unsigned length) +{ + if (!characters || !length) + return empty(); + + UChar* data; + RefPtr<StringImpl> string = createUninitialized(length, data); + memcpy(data, characters, length * sizeof(UChar)); + return string.release(); +} + +PassRefPtr<StringImpl> StringImpl::create(const LChar* characters, unsigned length) +{ + if (!characters || !length) + return empty(); + + LChar* data; + RefPtr<StringImpl> string = createUninitialized(length, data); + memcpy(data, characters, length * sizeof(LChar)); + return string.release(); +} + +PassRefPtr<StringImpl> StringImpl::create(const LChar* string) +{ + if (!string) + return empty(); + size_t length = strlen(reinterpret_cast<const char*>(string)); + if (length > numeric_limits<unsigned>::max()) + CRASH(); + return create(string, length); +} + +const UChar* StringImpl::getData16SlowCase() const +{ + if (has16BitShadow()) + return m_copyData16; + + if (bufferOwnership() == BufferSubstring) { + // If this is a substring, return a pointer into the parent string. + // TODO: Consider severing this string from the parent string + unsigned offset = m_data8 - m_substringBuffer->characters8(); + return m_substringBuffer->characters() + offset; + } + + unsigned len = length(); + if (hasTerminatingNullCharacter()) + len++; + + m_copyData16 = static_cast<UChar*>(fastMalloc(len * sizeof(UChar))); + + m_hashAndFlags |= s_hashFlagHas16BitShadow; + + upconvertCharacters(0, len); + + return m_copyData16; +} + +void StringImpl::upconvertCharacters(unsigned start, unsigned end) const +{ + ASSERT(is8Bit()); + ASSERT(has16BitShadow()); + + for (size_t i = start; i < end; i++) + m_copyData16[i] = m_data8[i]; +} + + +bool StringImpl::containsOnlyWhitespace() +{ + // FIXME: The definition of whitespace here includes a number of characters + // that are not whitespace from the point of view of RenderText; I wonder if + // that's a problem in practice. + if (is8Bit()) { + for (unsigned i = 0; i < m_length; i++) { + UChar c = m_data8[i]; + if (!isASCIISpace(c)) + return false; + } + + return true; + } + + for (unsigned i = 0; i < m_length; i++) { + UChar c = m_data16[i]; + if (!isASCIISpace(c)) + return false; + } + return true; +} + +PassRefPtr<StringImpl> StringImpl::substring(unsigned start, unsigned length) +{ + if (start >= m_length) + return empty(); + unsigned maxLength = m_length - start; + if (length >= maxLength) { + if (!start) + return this; + length = maxLength; + } + if (is8Bit()) + return create(m_data8 + start, length); + + return create(m_data16 + start, length); +} + +UChar32 StringImpl::characterStartingAt(unsigned i) +{ + if (is8Bit()) + return m_data8[i]; + if (U16_IS_SINGLE(m_data16[i])) + return m_data16[i]; + if (i + 1 < m_length && U16_IS_LEAD(m_data16[i]) && U16_IS_TRAIL(m_data16[i + 1])) + return U16_GET_SUPPLEMENTARY(m_data16[i], m_data16[i + 1]); + return 0; +} + +PassRefPtr<StringImpl> StringImpl::lower() +{ + // Note: This is a hot function in the Dromaeo benchmark, specifically the + // no-op code path up through the first 'return' statement. + + // First scan the string for uppercase and non-ASCII characters: + bool noUpper = true; + UChar ored = 0; + if (is8Bit()) { + const LChar* end = m_data8 + m_length; + for (const LChar* chp = m_data8; chp != end; chp++) { + if (UNLIKELY(isASCIIUpper(*chp))) + noUpper = false; + ored |= *chp; + } + // Nothing to do if the string is all ASCII with no uppercase. + if (noUpper && !(ored & ~0x7F)) + return this; + + if (m_length > static_cast<unsigned>(numeric_limits<int32_t>::max())) + CRASH(); + int32_t length = m_length; + + LChar* data8; + RefPtr<StringImpl> newImpl = createUninitialized(length, data8); + + if (!(ored & ~0x7F)) { + for (int32_t i = 0; i < length; i++) + data8[i] = toASCIILower(m_data8[i]); + + return newImpl.release(); + } + + // Do a slower implementation for cases that include non-ASCII Latin-1 characters. + for (int32_t i = 0; i < length; i++) + data8[i] = static_cast<LChar>(Unicode::toLower(m_data8[i])); + + return newImpl.release(); + } + + const UChar *end = m_data16 + m_length; + for (const UChar* chp = m_data16; chp != end; chp++) { + if (UNLIKELY(isASCIIUpper(*chp))) + noUpper = false; + ored |= *chp; + } + // Nothing to do if the string is all ASCII with no uppercase. + if (noUpper && !(ored & ~0x7F)) + return this; + + if (m_length > static_cast<unsigned>(numeric_limits<int32_t>::max())) + CRASH(); + int32_t length = m_length; + + if (!(ored & ~0x7F)) { + UChar* data16; + RefPtr<StringImpl> newImpl = createUninitialized(m_length, data16); + + for (int32_t i = 0; i < length; i++) { + UChar c = m_data16[i]; + data16[i] = toASCIILower(c); + } + return newImpl.release(); + } + + // Do a slower implementation for cases that include non-ASCII characters. + UChar* data16; + RefPtr<StringImpl> newImpl = createUninitialized(m_length, data16); + + bool error; + int32_t realLength = Unicode::toLower(data16, length, m_data16, m_length, &error); + if (!error && realLength == length) + return newImpl.release(); + + newImpl = createUninitialized(realLength, data16); + Unicode::toLower(data16, realLength, m_data16, m_length, &error); + if (error) + return this; + return newImpl.release(); +} + +PassRefPtr<StringImpl> StringImpl::upper() +{ + // This function could be optimized for no-op cases the way lower() is, + // but in empirical testing, few actual calls to upper() are no-ops, so + // it wouldn't be worth the extra time for pre-scanning. + + if (m_length > static_cast<unsigned>(numeric_limits<int32_t>::max())) + CRASH(); + int32_t length = m_length; + + if (is8Bit()) { + LChar* data8; + RefPtr<StringImpl> newImpl = createUninitialized(m_length, data8); + + // Do a faster loop for the case where all the characters are ASCII. + LChar ored = 0; + for (int i = 0; i < length; i++) { + LChar c = m_data8[i]; + ored |= c; + data8[i] = toASCIIUpper(c); + } + if (!(ored & ~0x7F)) + return newImpl.release(); + + // Do a slower implementation for cases that include non-ASCII Latin-1 characters. + for (int32_t i = 0; i < length; i++) + data8[i] = static_cast<LChar>(Unicode::toUpper(m_data8[i])); + + return newImpl.release(); + } + + UChar* data16; + RefPtr<StringImpl> newImpl = createUninitialized(m_length, data16); + + // Do a faster loop for the case where all the characters are ASCII. + UChar ored = 0; + for (int i = 0; i < length; i++) { + UChar c = m_data16[i]; + ored |= c; + data16[i] = toASCIIUpper(c); + } + if (!(ored & ~0x7F)) + return newImpl.release(); + + // Do a slower implementation for cases that include non-ASCII characters. + bool error; + newImpl = createUninitialized(m_length, data16); + int32_t realLength = Unicode::toUpper(data16, length, m_data16, m_length, &error); + if (!error && realLength == length) + return newImpl; + newImpl = createUninitialized(realLength, data16); + Unicode::toUpper(data16, realLength, m_data16, m_length, &error); + if (error) + return this; + return newImpl.release(); +} + +PassRefPtr<StringImpl> StringImpl::fill(UChar character) +{ + if (!m_length) + return this; + + if (!(character & ~0x7F)) { + LChar* data; + RefPtr<StringImpl> newImpl = createUninitialized(m_length, data); + for (unsigned i = 0; i < m_length; ++i) + data[i] = character; + return newImpl.release(); + } + UChar* data; + RefPtr<StringImpl> newImpl = createUninitialized(m_length, data); + for (unsigned i = 0; i < m_length; ++i) + data[i] = character; + return newImpl.release(); +} + +PassRefPtr<StringImpl> StringImpl::foldCase() +{ + if (m_length > static_cast<unsigned>(numeric_limits<int32_t>::max())) + CRASH(); + int32_t length = m_length; + + if (is8Bit()) { + // Do a faster loop for the case where all the characters are ASCII. + LChar* data; + RefPtr <StringImpl>newImpl = createUninitialized(m_length, data); + LChar ored = 0; + + for (int32_t i = 0; i < length; i++) { + LChar c = m_data8[i]; + data[i] = toASCIILower(c); + ored |= c; + } + + if (!(ored & ~0x7F)) + return newImpl.release(); + + // Do a slower implementation for cases that include non-ASCII Latin-1 characters. + for (int32_t i = 0; i < length; i++) + data[i] = static_cast<LChar>(Unicode::toLower(m_data8[i])); + + return newImpl.release(); + } + + // Do a faster loop for the case where all the characters are ASCII. + UChar* data; + RefPtr<StringImpl> newImpl = createUninitialized(m_length, data); + UChar ored = 0; + for (int32_t i = 0; i < length; i++) { + UChar c = m_data16[i]; + ored |= c; + data[i] = toASCIILower(c); + } + if (!(ored & ~0x7F)) + return newImpl.release(); + + // Do a slower implementation for cases that include non-ASCII characters. + bool error; + int32_t realLength = Unicode::foldCase(data, length, m_data16, m_length, &error); + if (!error && realLength == length) + return newImpl.release(); + newImpl = createUninitialized(realLength, data); + Unicode::foldCase(data, realLength, m_data16, m_length, &error); + if (error) + return this; + return newImpl.release(); +} + +template <class UCharPredicate> +inline PassRefPtr<StringImpl> StringImpl::stripMatchedCharacters(UCharPredicate predicate) +{ + if (!m_length) + return empty(); + + unsigned start = 0; + unsigned end = m_length - 1; + + // skip white space from start + while (start <= end && predicate(is8Bit() ? m_data8[start] : m_data16[start])) + start++; + + // only white space + if (start > end) + return empty(); + + // skip white space from end + while (end && predicate(is8Bit() ? m_data8[end] : m_data16[end])) + end--; + + if (!start && end == m_length - 1) + return this; + if (is8Bit()) + return create(m_data8 + start, end + 1 - start); + return create(m_data16 + start, end + 1 - start); +} + +class UCharPredicate { +public: + inline UCharPredicate(CharacterMatchFunctionPtr function): m_function(function) { } + + inline bool operator()(UChar ch) const + { + return m_function(ch); + } + +private: + const CharacterMatchFunctionPtr m_function; +}; + +class SpaceOrNewlinePredicate { +public: + inline bool operator()(UChar ch) const + { + return isSpaceOrNewline(ch); + } +}; + +PassRefPtr<StringImpl> StringImpl::stripWhiteSpace() +{ + return stripMatchedCharacters(SpaceOrNewlinePredicate()); +} + +PassRefPtr<StringImpl> StringImpl::stripWhiteSpace(IsWhiteSpaceFunctionPtr isWhiteSpace) +{ + return stripMatchedCharacters(UCharPredicate(isWhiteSpace)); +} + +template <typename CharType> +ALWAYS_INLINE PassRefPtr<StringImpl> StringImpl::removeCharacters(const CharType* characters, CharacterMatchFunctionPtr findMatch) +{ + const CharType* from = characters; + const CharType* fromend = from + m_length; + + // Assume the common case will not remove any characters + while (from != fromend && !findMatch(*from)) + from++; + if (from == fromend) + return this; + + StringBuffer<CharType> data(m_length); + CharType* to = data.characters(); + unsigned outc = from - characters; + + if (outc) + memcpy(to, characters, outc * sizeof(CharType)); + + while (true) { + while (from != fromend && findMatch(*from)) + from++; + while (from != fromend && !findMatch(*from)) + to[outc++] = *from++; + if (from == fromend) + break; + } + + data.shrink(outc); + + return adopt(data); +} + +PassRefPtr<StringImpl> StringImpl::removeCharacters(CharacterMatchFunctionPtr findMatch) +{ + if (is8Bit()) + return removeCharacters(characters8(), findMatch); + return removeCharacters(characters16(), findMatch); +} + +template <typename CharType, class UCharPredicate> +inline PassRefPtr<StringImpl> StringImpl::simplifyMatchedCharactersToSpace(UCharPredicate predicate) +{ + StringBuffer<CharType> data(m_length); + + const CharType* from = getCharacters<CharType>(); + const CharType* fromend = from + m_length; + int outc = 0; + bool changedToSpace = false; + + CharType* to = data.characters(); + + while (true) { + while (from != fromend && predicate(*from)) { + if (*from != ' ') + changedToSpace = true; + from++; + } + while (from != fromend && !predicate(*from)) + to[outc++] = *from++; + if (from != fromend) + to[outc++] = ' '; + else + break; + } + + if (outc > 0 && to[outc - 1] == ' ') + outc--; + + if (static_cast<unsigned>(outc) == m_length && !changedToSpace) + return this; + + data.shrink(outc); + + return adopt(data); +} + +PassRefPtr<StringImpl> StringImpl::simplifyWhiteSpace() +{ + if (is8Bit()) + return StringImpl::simplifyMatchedCharactersToSpace<LChar>(SpaceOrNewlinePredicate()); + return StringImpl::simplifyMatchedCharactersToSpace<UChar>(SpaceOrNewlinePredicate()); +} + +PassRefPtr<StringImpl> StringImpl::simplifyWhiteSpace(IsWhiteSpaceFunctionPtr isWhiteSpace) +{ + if (is8Bit()) + return StringImpl::simplifyMatchedCharactersToSpace<LChar>(UCharPredicate(isWhiteSpace)); + return StringImpl::simplifyMatchedCharactersToSpace<UChar>(UCharPredicate(isWhiteSpace)); +} + +int StringImpl::toIntStrict(bool* ok, int base) +{ + if (is8Bit()) + return charactersToIntStrict(characters8(), m_length, ok, base); + return charactersToIntStrict(characters16(), m_length, ok, base); +} + +unsigned StringImpl::toUIntStrict(bool* ok, int base) +{ + if (is8Bit()) + return charactersToUIntStrict(characters8(), m_length, ok, base); + return charactersToUIntStrict(characters16(), m_length, ok, base); +} + +int64_t StringImpl::toInt64Strict(bool* ok, int base) +{ + if (is8Bit()) + return charactersToInt64Strict(characters8(), m_length, ok, base); + return charactersToInt64Strict(characters16(), m_length, ok, base); +} + +uint64_t StringImpl::toUInt64Strict(bool* ok, int base) +{ + if (is8Bit()) + return charactersToUInt64Strict(characters8(), m_length, ok, base); + return charactersToUInt64Strict(characters16(), m_length, ok, base); +} + +intptr_t StringImpl::toIntPtrStrict(bool* ok, int base) +{ + if (is8Bit()) + return charactersToIntPtrStrict(characters8(), m_length, ok, base); + return charactersToIntPtrStrict(characters16(), m_length, ok, base); +} + +int StringImpl::toInt(bool* ok) +{ + if (is8Bit()) + return charactersToInt(characters8(), m_length, ok); + return charactersToInt(characters16(), m_length, ok); +} + +unsigned StringImpl::toUInt(bool* ok) +{ + if (is8Bit()) + return charactersToUInt(characters8(), m_length, ok); + return charactersToUInt(characters16(), m_length, ok); +} + +int64_t StringImpl::toInt64(bool* ok) +{ + if (is8Bit()) + return charactersToInt64(characters8(), m_length, ok); + return charactersToInt64(characters16(), m_length, ok); +} + +uint64_t StringImpl::toUInt64(bool* ok) +{ + if (is8Bit()) + return charactersToUInt64(characters8(), m_length, ok); + return charactersToUInt64(characters16(), m_length, ok); +} + +intptr_t StringImpl::toIntPtr(bool* ok) +{ + if (is8Bit()) + return charactersToIntPtr(characters8(), m_length, ok); + return charactersToIntPtr(characters16(), m_length, ok); +} + +double StringImpl::toDouble(bool* ok, bool* didReadNumber) +{ + if (is8Bit()) + return charactersToDouble(characters8(), m_length, ok, didReadNumber); + return charactersToDouble(characters16(), m_length, ok, didReadNumber); +} + +float StringImpl::toFloat(bool* ok, bool* didReadNumber) +{ + if (is8Bit()) + return charactersToFloat(characters8(), m_length, ok, didReadNumber); + return charactersToFloat(characters16(), m_length, ok, didReadNumber); +} + +bool equalIgnoringCase(const UChar* a, const LChar* b, unsigned length) +{ + while (length--) { + LChar bc = *b++; + if (foldCase(*a++) != foldCase(bc)) + return false; + } + return true; +} + +static inline bool equalIgnoringCase(const UChar* a, const UChar* b, int length) +{ + ASSERT(length >= 0); + return umemcasecmp(a, b, length) == 0; +} + +int codePointCompare(const StringImpl* s1, const StringImpl* s2) +{ + const unsigned l1 = s1 ? s1->length() : 0; + const unsigned l2 = s2 ? s2->length() : 0; + const unsigned lmin = l1 < l2 ? l1 : l2; + const UChar* c1 = s1 ? s1->characters() : 0; + const UChar* c2 = s2 ? s2->characters() : 0; + unsigned pos = 0; + while (pos < lmin && *c1 == *c2) { + c1++; + c2++; + pos++; + } + + if (pos < lmin) + return (c1[0] > c2[0]) ? 1 : -1; + + if (l1 == l2) + return 0; + + return (l1 > l2) ? 1 : -1; +} + +size_t StringImpl::find(UChar c, unsigned start) +{ + if (is8Bit()) + return WTF::find(characters8(), m_length, c, start); + return WTF::find(characters16(), m_length, c, start); +} + +size_t StringImpl::find(CharacterMatchFunctionPtr matchFunction, unsigned start) +{ + if (is8Bit()) + return WTF::find(characters8(), m_length, matchFunction, start); + return WTF::find(characters16(), m_length, matchFunction, start); +} + +size_t StringImpl::find(const LChar* matchString, unsigned index) +{ + // Check for null or empty string to match against + if (!matchString) + return notFound; + size_t matchStringLength = strlen(reinterpret_cast<const char*>(matchString)); + if (matchStringLength > numeric_limits<unsigned>::max()) + CRASH(); + unsigned matchLength = matchStringLength; + if (!matchLength) + return min(index, length()); + + // Optimization 1: fast case for strings of length 1. + if (matchLength == 1) + return WTF::find(characters16(), length(), *matchString, index); + + // Check index & matchLength are in range. + if (index > length()) + return notFound; + unsigned searchLength = length() - index; + if (matchLength > searchLength) + return notFound; + // delta is the number of additional times to test; delta == 0 means test only once. + unsigned delta = searchLength - matchLength; + + const UChar* searchCharacters = characters() + index; + + // Optimization 2: keep a running hash of the strings, + // only call memcmp if the hashes match. + unsigned searchHash = 0; + unsigned matchHash = 0; + for (unsigned i = 0; i < matchLength; ++i) { + searchHash += searchCharacters[i]; + matchHash += matchString[i]; + } + + unsigned i = 0; + // keep looping until we match + while (searchHash != matchHash || !equal(searchCharacters + i, matchString, matchLength)) { + if (i == delta) + return notFound; + searchHash += searchCharacters[i + matchLength]; + searchHash -= searchCharacters[i]; + ++i; + } + return index + i; +} + +size_t StringImpl::findIgnoringCase(const LChar* matchString, unsigned index) +{ + // Check for null or empty string to match against + if (!matchString) + return notFound; + size_t matchStringLength = strlen(reinterpret_cast<const char*>(matchString)); + if (matchStringLength > numeric_limits<unsigned>::max()) + CRASH(); + unsigned matchLength = matchStringLength; + if (!matchLength) + return min(index, length()); + + // Check index & matchLength are in range. + if (index > length()) + return notFound; + unsigned searchLength = length() - index; + if (matchLength > searchLength) + return notFound; + // delta is the number of additional times to test; delta == 0 means test only once. + unsigned delta = searchLength - matchLength; + + const UChar* searchCharacters = characters() + index; + + unsigned i = 0; + // keep looping until we match + while (!equalIgnoringCase(searchCharacters + i, matchString, matchLength)) { + if (i == delta) + return notFound; + ++i; + } + return index + i; +} + +size_t StringImpl::find(StringImpl* matchString, unsigned index) +{ + // Check for null or empty string to match against + if (!matchString) + return notFound; + unsigned matchLength = matchString->length(); + if (!matchLength) + return min(index, length()); + + // Optimization 1: fast case for strings of length 1. + if (matchLength == 1) { + if (is8Bit() && matchString->is8Bit()) + return WTF::find(characters8(), length(), matchString->characters8()[0], index); + return WTF::find(characters(), length(), matchString->characters()[0], index); + } + + // Check index & matchLength are in range. + if (index > length()) + return notFound; + unsigned searchLength = length() - index; + if (matchLength > searchLength) + return notFound; + // delta is the number of additional times to test; delta == 0 means test only once. + unsigned delta = searchLength - matchLength; + + const UChar* searchCharacters = characters() + index; + const UChar* matchCharacters = matchString->characters(); + + // Optimization 2: keep a running hash of the strings, + // only call memcmp if the hashes match. + unsigned searchHash = 0; + unsigned matchHash = 0; + for (unsigned i = 0; i < matchLength; ++i) { + searchHash += searchCharacters[i]; + matchHash += matchCharacters[i]; + } + + unsigned i = 0; + // keep looping until we match + while (searchHash != matchHash || memcmp(searchCharacters + i, matchCharacters, matchLength * sizeof(UChar))) { + if (i == delta) + return notFound; + searchHash += searchCharacters[i + matchLength]; + searchHash -= searchCharacters[i]; + ++i; + } + return index + i; +} + +size_t StringImpl::findIgnoringCase(StringImpl* matchString, unsigned index) +{ + // Check for null or empty string to match against + if (!matchString) + return notFound; + unsigned matchLength = matchString->length(); + if (!matchLength) + return min(index, length()); + + // Check index & matchLength are in range. + if (index > length()) + return notFound; + unsigned searchLength = length() - index; + if (matchLength > searchLength) + return notFound; + // delta is the number of additional times to test; delta == 0 means test only once. + unsigned delta = searchLength - matchLength; + + const UChar* searchCharacters = characters() + index; + const UChar* matchCharacters = matchString->characters(); + + unsigned i = 0; + // keep looping until we match + while (!equalIgnoringCase(searchCharacters + i, matchCharacters, matchLength)) { + if (i == delta) + return notFound; + ++i; + } + return index + i; +} + +size_t StringImpl::reverseFind(UChar c, unsigned index) +{ + if (is8Bit()) + return WTF::reverseFind(characters8(), m_length, c, index); + return WTF::reverseFind(characters16(), m_length, c, index); +} + +size_t StringImpl::reverseFind(StringImpl* matchString, unsigned index) +{ + // Check for null or empty string to match against + if (!matchString) + return notFound; + unsigned matchLength = matchString->length(); + if (!matchLength) + return min(index, length()); + + // Optimization 1: fast case for strings of length 1. + if (matchLength == 1) { + if (is8Bit() && matchString->is8Bit()) + return WTF::reverseFind(characters8(), length(), matchString->characters8()[0], index); + return WTF::reverseFind(characters(), length(), matchString->characters()[0], index); + } + + // Check index & matchLength are in range. + if (matchLength > length()) + return notFound; + // delta is the number of additional times to test; delta == 0 means test only once. + unsigned delta = min(index, length() - matchLength); + + const UChar *searchCharacters = characters(); + const UChar *matchCharacters = matchString->characters(); + + // Optimization 2: keep a running hash of the strings, + // only call memcmp if the hashes match. + unsigned searchHash = 0; + unsigned matchHash = 0; + for (unsigned i = 0; i < matchLength; ++i) { + searchHash += searchCharacters[delta + i]; + matchHash += matchCharacters[i]; + } + + // keep looping until we match + while (searchHash != matchHash || memcmp(searchCharacters + delta, matchCharacters, matchLength * sizeof(UChar))) { + if (!delta) + return notFound; + delta--; + searchHash -= searchCharacters[delta + matchLength]; + searchHash += searchCharacters[delta]; + } + return delta; +} + +size_t StringImpl::reverseFindIgnoringCase(StringImpl* matchString, unsigned index) +{ + // Check for null or empty string to match against + if (!matchString) + return notFound; + unsigned matchLength = matchString->length(); + if (!matchLength) + return min(index, length()); + + // Check index & matchLength are in range. + if (matchLength > length()) + return notFound; + // delta is the number of additional times to test; delta == 0 means test only once. + unsigned delta = min(index, length() - matchLength); + + const UChar *searchCharacters = characters(); + const UChar *matchCharacters = matchString->characters(); + + // keep looping until we match + while (!equalIgnoringCase(searchCharacters + delta, matchCharacters, matchLength)) { + if (!delta) + return notFound; + delta--; + } + return delta; +} + +bool StringImpl::endsWith(StringImpl* matchString, bool caseSensitive) +{ + ASSERT(matchString); + if (m_length >= matchString->m_length) { + unsigned start = m_length - matchString->m_length; + return (caseSensitive ? find(matchString, start) : findIgnoringCase(matchString, start)) == start; + } + return false; +} + +PassRefPtr<StringImpl> StringImpl::replace(UChar oldC, UChar newC) +{ + if (oldC == newC) + return this; + unsigned i; + for (i = 0; i != m_length; ++i) { + UChar c = is8Bit() ? m_data8[i] : m_data16[i]; + if (c == oldC) + break; + } + if (i == m_length) + return this; + + if (is8Bit()) { + if (oldC > 0xff) + // Looking for a 16 bit char in an 8 bit string, we're done. + return this; + + if (newC <= 0xff) { + LChar* data; + LChar oldChar = static_cast<LChar>(oldC); + LChar newChar = static_cast<LChar>(newC); + + RefPtr<StringImpl> newImpl = createUninitialized(m_length, data); + + for (i = 0; i != m_length; ++i) { + LChar ch = m_data8[i]; + if (ch == oldChar) + ch = newChar; + data[i] = ch; + } + return newImpl.release(); + } + + // There is the possibility we need to up convert from 8 to 16 bit, + // create a 16 bit string for the result. + UChar* data; + RefPtr<StringImpl> newImpl = createUninitialized(m_length, data); + + for (i = 0; i != m_length; ++i) { + UChar ch = m_data8[i]; + if (ch == oldC) + ch = newC; + data[i] = ch; + } + + return newImpl.release(); + } + + UChar* data; + RefPtr<StringImpl> newImpl = createUninitialized(m_length, data); + + for (i = 0; i != m_length; ++i) { + UChar ch = m_data16[i]; + if (ch == oldC) + ch = newC; + data[i] = ch; + } + return newImpl.release(); +} + +PassRefPtr<StringImpl> StringImpl::replace(unsigned position, unsigned lengthToReplace, StringImpl* str) +{ + position = min(position, length()); + lengthToReplace = min(lengthToReplace, length() - position); + unsigned lengthToInsert = str ? str->length() : 0; + if (!lengthToReplace && !lengthToInsert) + return this; + + if ((length() - lengthToReplace) >= (numeric_limits<unsigned>::max() - lengthToInsert)) + CRASH(); + + if (is8Bit() && (!str || str->is8Bit())) { + LChar* data; + RefPtr<StringImpl> newImpl = + createUninitialized(length() - lengthToReplace + lengthToInsert, data); + memcpy(data, m_data8, position * sizeof(LChar)); + if (str) + memcpy(data + position, str->m_data8, lengthToInsert * sizeof(LChar)); + memcpy(data + position + lengthToInsert, m_data8 + position + lengthToReplace, + (length() - position - lengthToReplace) * sizeof(LChar)); + return newImpl.release(); + } + UChar* data; + RefPtr<StringImpl> newImpl = + createUninitialized(length() - lengthToReplace + lengthToInsert, data); + if (is8Bit()) + for (unsigned i = 0; i < position; i++) + data[i] = m_data8[i]; + else + memcpy(data, m_data16, position * sizeof(UChar)); + if (str) { + if (str->is8Bit()) + for (unsigned i = 0; i < lengthToInsert; i++) + data[i + position] = str->m_data8[i]; + else + memcpy(data + position, str->m_data16, lengthToInsert * sizeof(UChar)); + } + if (is8Bit()) { + for (unsigned i = 0; i < length() - position - lengthToReplace; i++) + data[i + position + lengthToInsert] = m_data8[i + position + lengthToReplace]; + } else { + memcpy(data + position + lengthToInsert, characters() + position + lengthToReplace, + (length() - position - lengthToReplace) * sizeof(UChar)); + } + return newImpl.release(); +} + +PassRefPtr<StringImpl> StringImpl::replace(UChar pattern, StringImpl* replacement) +{ + if (!replacement) + return this; + + unsigned repStrLength = replacement->length(); + size_t srcSegmentStart = 0; + unsigned matchCount = 0; + + // Count the matches. + while ((srcSegmentStart = find(pattern, srcSegmentStart)) != notFound) { + ++matchCount; + ++srcSegmentStart; + } + + // If we have 0 matches then we don't have to do any more work. + if (!matchCount) + return this; + + if (repStrLength && matchCount > numeric_limits<unsigned>::max() / repStrLength) + CRASH(); + + unsigned replaceSize = matchCount * repStrLength; + unsigned newSize = m_length - matchCount; + if (newSize >= (numeric_limits<unsigned>::max() - replaceSize)) + CRASH(); + + newSize += replaceSize; + + // Construct the new data. + size_t srcSegmentEnd; + unsigned srcSegmentLength; + srcSegmentStart = 0; + unsigned dstOffset = 0; + bool srcIs8Bit = is8Bit(); + bool replacementIs8Bit = replacement->is8Bit(); + + // There are 4 cases: + // 1. This and replacement are both 8 bit. + // 2. This and replacement are both 16 bit. + // 3. This is 8 bit and replacement is 16 bit. + // 4. This is 16 bit and replacement is 8 bit. + if (srcIs8Bit && replacementIs8Bit) { + // Case 1 + LChar* data; + RefPtr<StringImpl> newImpl = createUninitialized(newSize, data); + + while ((srcSegmentEnd = find(pattern, srcSegmentStart)) != notFound) { + srcSegmentLength = srcSegmentEnd - srcSegmentStart; + memcpy(data + dstOffset, m_data8 + srcSegmentStart, srcSegmentLength * sizeof(LChar)); + dstOffset += srcSegmentLength; + memcpy(data + dstOffset, replacement->m_data8, repStrLength * sizeof(LChar)); + dstOffset += repStrLength; + srcSegmentStart = srcSegmentEnd + 1; + } + + srcSegmentLength = m_length - srcSegmentStart; + memcpy(data + dstOffset, m_data8 + srcSegmentStart, srcSegmentLength * sizeof(LChar)); + + ASSERT(dstOffset + srcSegmentLength == newImpl->length()); + + return newImpl.release(); + } + + UChar* data; + RefPtr<StringImpl> newImpl = createUninitialized(newSize, data); + + while ((srcSegmentEnd = find(pattern, srcSegmentStart)) != notFound) { + srcSegmentLength = srcSegmentEnd - srcSegmentStart; + if (srcIs8Bit) { + // Case 3. + for (unsigned i = 0; i < srcSegmentLength; i++) + data[i + dstOffset] = m_data8[i + srcSegmentStart]; + } else { + // Cases 2 & 4. + memcpy(data + dstOffset, m_data16 + srcSegmentStart, srcSegmentLength * sizeof(UChar)); + } + dstOffset += srcSegmentLength; + if (replacementIs8Bit) { + // Case 4. + for (unsigned i = 0; i < repStrLength; i++) + data[i + dstOffset] = replacement->m_data8[i]; + } else { + // Cases 2 & 3. + memcpy(data + dstOffset, replacement->m_data16, repStrLength * sizeof(UChar)); + } + dstOffset += repStrLength; + srcSegmentStart = srcSegmentEnd + 1; + } + + srcSegmentLength = m_length - srcSegmentStart; + if (srcIs8Bit) { + // Case 3. + for (unsigned i = 0; i < srcSegmentLength; i++) + data[i + dstOffset] = m_data8[i + srcSegmentStart]; + } else { + // Cases 2 & 4. + memcpy(data + dstOffset, m_data16 + srcSegmentStart, srcSegmentLength * sizeof(UChar)); + } + + ASSERT(dstOffset + srcSegmentLength == newImpl->length()); + + return newImpl.release(); +} + +PassRefPtr<StringImpl> StringImpl::replace(StringImpl* pattern, StringImpl* replacement) +{ + if (!pattern || !replacement) + return this; + + unsigned patternLength = pattern->length(); + if (!patternLength) + return this; + + unsigned repStrLength = replacement->length(); + size_t srcSegmentStart = 0; + unsigned matchCount = 0; + + // Count the matches. + while ((srcSegmentStart = find(pattern, srcSegmentStart)) != notFound) { + ++matchCount; + srcSegmentStart += patternLength; + } + + // If we have 0 matches, we don't have to do any more work + if (!matchCount) + return this; + + unsigned newSize = m_length - matchCount * patternLength; + if (repStrLength && matchCount > numeric_limits<unsigned>::max() / repStrLength) + CRASH(); + + if (newSize > (numeric_limits<unsigned>::max() - matchCount * repStrLength)) + CRASH(); + + newSize += matchCount * repStrLength; + + + // Construct the new data + size_t srcSegmentEnd; + unsigned srcSegmentLength; + srcSegmentStart = 0; + unsigned dstOffset = 0; + bool srcIs8Bit = is8Bit(); + bool replacementIs8Bit = replacement->is8Bit(); + + // There are 4 cases: + // 1. This and replacement are both 8 bit. + // 2. This and replacement are both 16 bit. + // 3. This is 8 bit and replacement is 16 bit. + // 4. This is 16 bit and replacement is 8 bit. + if (srcIs8Bit && replacementIs8Bit) { + // Case 1 + LChar* data; + RefPtr<StringImpl> newImpl = createUninitialized(newSize, data); + while ((srcSegmentEnd = find(pattern, srcSegmentStart)) != notFound) { + srcSegmentLength = srcSegmentEnd - srcSegmentStart; + memcpy(data + dstOffset, m_data8 + srcSegmentStart, srcSegmentLength * sizeof(LChar)); + dstOffset += srcSegmentLength; + memcpy(data + dstOffset, replacement->m_data8, repStrLength * sizeof(LChar)); + dstOffset += repStrLength; + srcSegmentStart = srcSegmentEnd + patternLength; + } + + srcSegmentLength = m_length - srcSegmentStart; + memcpy(data + dstOffset, m_data8 + srcSegmentStart, srcSegmentLength * sizeof(LChar)); + + ASSERT(dstOffset + srcSegmentLength == newImpl->length()); + + return newImpl.release(); + } + + UChar* data; + RefPtr<StringImpl> newImpl = createUninitialized(newSize, data); + while ((srcSegmentEnd = find(pattern, srcSegmentStart)) != notFound) { + srcSegmentLength = srcSegmentEnd - srcSegmentStart; + if (srcIs8Bit) { + // Case 3. + for (unsigned i = 0; i < srcSegmentLength; i++) + data[i + dstOffset] = m_data8[i + srcSegmentStart]; + } else { + // Case 2 & 4. + memcpy(data + dstOffset, m_data16 + srcSegmentStart, srcSegmentLength * sizeof(UChar)); + } + dstOffset += srcSegmentLength; + if (replacementIs8Bit) { + // Cases 2 & 3. + for (unsigned i = 0; i < repStrLength; i++) + data[i + dstOffset] = replacement->m_data8[i]; + } else { + // Case 4 + memcpy(data + dstOffset, replacement->m_data16, repStrLength * sizeof(UChar)); + } + dstOffset += repStrLength; + srcSegmentStart = srcSegmentEnd + patternLength; + } + + srcSegmentLength = m_length - srcSegmentStart; + if (srcIs8Bit) { + // Case 3. + for (unsigned i = 0; i < srcSegmentLength; i++) + data[i + dstOffset] = m_data8[i + srcSegmentStart]; + } else { + // Cases 2 & 4. + memcpy(data + dstOffset, m_data16 + srcSegmentStart, srcSegmentLength * sizeof(UChar)); + } + + ASSERT(dstOffset + srcSegmentLength == newImpl->length()); + + return newImpl.release(); +} + +bool equal(const StringImpl* a, const StringImpl* b) +{ + return StringHash::equal(a, b); +} + +bool equal(const StringImpl* a, const LChar* b, unsigned length) +{ + if (!a) + return !b; + if (!b) + return !a; + + if (length != a->length()) + return false; + + if (a->is8Bit()) + return equal(a->characters8(), b, length); + return equal(a->characters16(), b, length); +} + +bool equal(const StringImpl* a, const LChar* b) +{ + if (!a) + return !b; + if (!b) + return !a; + + unsigned length = a->length(); + + if (a->is8Bit()) { + const LChar* aPtr = a->characters8(); + for (unsigned i = 0; i != length; ++i) { + LChar bc = b[i]; + LChar ac = aPtr[i]; + if (!bc) + return false; + if (ac != bc) + return false; + } + + return !b[length]; + } + + const UChar* aPtr = a->characters16(); + for (unsigned i = 0; i != length; ++i) { + LChar bc = b[i]; + if (!bc) + return false; + if (aPtr[i] != bc) + return false; + } + + return !b[length]; +} + +bool equal(const StringImpl* a, const UChar* b, unsigned length) +{ + if (!a) + return !b; + if (!b) + return false; + + if (a->length() != length) + return false; + if (a->is8Bit()) + return equal(a->characters8(), b, length); + return equal(a->characters16(), b, length); +} + +bool equalIgnoringCase(StringImpl* a, StringImpl* b) +{ + return CaseFoldingHash::equal(a, b); +} + +bool equalIgnoringCase(StringImpl* a, const LChar* b) +{ + if (!a) + return !b; + if (!b) + return !a; + + unsigned length = a->length(); + + // Do a faster loop for the case where all the characters are ASCII. + UChar ored = 0; + bool equal = true; + if (a->is8Bit()) { + const LChar* as = a->characters8(); + for (unsigned i = 0; i != length; ++i) { + LChar bc = b[i]; + if (!bc) + return false; + UChar ac = as[i]; + ored |= ac; + equal = equal && (toASCIILower(ac) == toASCIILower(bc)); + } + + // Do a slower implementation for cases that include non-ASCII characters. + if (ored & ~0x7F) { + equal = true; + for (unsigned i = 0; i != length; ++i) + equal = equal && (foldCase(as[i]) == foldCase(b[i])); + } + + return equal && !b[length]; + } + + const UChar* as = a->characters16(); + for (unsigned i = 0; i != length; ++i) { + LChar bc = b[i]; + if (!bc) + return false; + UChar ac = as[i]; + ored |= ac; + equal = equal && (toASCIILower(ac) == toASCIILower(bc)); + } + + // Do a slower implementation for cases that include non-ASCII characters. + if (ored & ~0x7F) { + equal = true; + for (unsigned i = 0; i != length; ++i) { + equal = equal && (foldCase(as[i]) == foldCase(b[i])); + } + } + + return equal && !b[length]; +} + +bool equalIgnoringNullity(StringImpl* a, StringImpl* b) +{ + if (StringHash::equal(a, b)) + return true; + if (!a && b && !b->length()) + return true; + if (!b && a && !a->length()) + return true; + + return false; +} + +WTF::Unicode::Direction StringImpl::defaultWritingDirection(bool* hasStrongDirectionality) +{ + for (unsigned i = 0; i < m_length; ++i) { + WTF::Unicode::Direction charDirection = WTF::Unicode::direction(is8Bit() ? m_data8[i] : m_data16[i]); + if (charDirection == WTF::Unicode::LeftToRight) { + if (hasStrongDirectionality) + *hasStrongDirectionality = true; + return WTF::Unicode::LeftToRight; + } + if (charDirection == WTF::Unicode::RightToLeft || charDirection == WTF::Unicode::RightToLeftArabic) { + if (hasStrongDirectionality) + *hasStrongDirectionality = true; + return WTF::Unicode::RightToLeft; + } + } + if (hasStrongDirectionality) + *hasStrongDirectionality = false; + return WTF::Unicode::LeftToRight; +} + +PassRefPtr<StringImpl> StringImpl::adopt(StringBuffer<LChar>& buffer) +{ +unsigned length = buffer.length(); +if (!length) + return empty(); +return adoptRef(new StringImpl(buffer.release(), length)); +} + +PassRefPtr<StringImpl> StringImpl::adopt(StringBuffer<UChar>& buffer) +{ + unsigned length = buffer.length(); + if (!length) + return empty(); + return adoptRef(new StringImpl(buffer.release(), length)); +} + +PassRefPtr<StringImpl> StringImpl::createWithTerminatingNullCharacter(const StringImpl& string) +{ + // Use createUninitialized instead of 'new StringImpl' so that the string and its buffer + // get allocated in a single memory block. + unsigned length = string.m_length; + if (length >= numeric_limits<unsigned>::max()) + CRASH(); + RefPtr<StringImpl> terminatedString; + if (string.is8Bit()) { + LChar* data; + terminatedString = createUninitialized(length + 1, data); + memcpy(data, string.m_data8, length * sizeof(LChar)); + data[length] = 0; + } else { + UChar* data; + terminatedString = createUninitialized(length + 1, data); + memcpy(data, string.m_data16, length * sizeof(UChar)); + data[length] = 0; + } + terminatedString->m_length--; + terminatedString->m_hashAndFlags = (string.m_hashAndFlags & (~s_flagMask | s_hashFlag8BitBuffer)) | s_hashFlagHasTerminatingNullCharacter; + return terminatedString.release(); +} + +} // namespace WTF diff --git a/Source/JavaScriptCore/wtf/text/StringImpl.h b/Source/JavaScriptCore/wtf/text/StringImpl.h new file mode 100644 index 000000000..a3008e1d3 --- /dev/null +++ b/Source/JavaScriptCore/wtf/text/StringImpl.h @@ -0,0 +1,774 @@ +/* + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010 Apple Inc. All rights reserved. + * Copyright (C) 2009 Google Inc. All rights reserved. + * + * This library 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 library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#ifndef StringImpl_h +#define StringImpl_h + +#include <limits.h> +#include <wtf/ASCIICType.h> +#include <wtf/Forward.h> +#include <wtf/OwnFastMallocPtr.h> +#include <wtf/StdLibExtras.h> +#include <wtf/StringHasher.h> +#include <wtf/Vector.h> +#include <wtf/unicode/Unicode.h> + +#if USE(CF) +typedef const struct __CFString * CFStringRef; +#endif + +#ifdef __OBJC__ +@class NSString; +#endif + +// FIXME: This is a temporary layering violation while we move string code to WTF. +// Landing the file moves in one patch, will follow on with patches to change the namespaces. +namespace JSC { +struct IdentifierCStringTranslator; +template <typename T> struct IdentifierCharBufferTranslator; +struct IdentifierLCharFromUCharTranslator; +} + +namespace WTF { + +struct CStringTranslator; +struct HashAndCharactersTranslator; +struct HashAndUTF8CharactersTranslator; +struct UCharBufferTranslator; + +enum TextCaseSensitivity { TextCaseSensitive, TextCaseInsensitive }; + +typedef bool (*CharacterMatchFunctionPtr)(UChar); +typedef bool (*IsWhiteSpaceFunctionPtr)(UChar); + +class StringImpl { + WTF_MAKE_NONCOPYABLE(StringImpl); WTF_MAKE_FAST_ALLOCATED; + friend struct JSC::IdentifierCStringTranslator; + friend struct JSC::IdentifierCharBufferTranslator<LChar>; + friend struct JSC::IdentifierCharBufferTranslator<UChar>; + friend struct JSC::IdentifierLCharFromUCharTranslator; + friend struct WTF::CStringTranslator; + friend struct WTF::HashAndCharactersTranslator; + friend struct WTF::HashAndUTF8CharactersTranslator; + friend struct WTF::UCharBufferTranslator; + friend class AtomicStringImpl; + +private: + enum BufferOwnership { + BufferInternal, + BufferOwned, + BufferSubstring, + }; + + // Used to construct static strings, which have an special refCount that can never hit zero. + // This means that the static string will never be destroyed, which is important because + // static strings will be shared across threads & ref-counted in a non-threadsafe manner. + enum ConstructStaticStringTag { ConstructStaticString }; + StringImpl(const UChar* characters, unsigned length, ConstructStaticStringTag) + : m_refCount(s_refCountFlagIsStaticString) + , m_length(length) + , m_data16(characters) + , m_buffer(0) + , m_hashAndFlags(s_hashFlagIsIdentifier | BufferOwned) + { + // Ensure that the hash is computed so that AtomicStringHash can call existingHash() + // with impunity. The empty string is special because it is never entered into + // AtomicString's HashKey, but still needs to compare correctly. + hash(); + } + + // Used to construct static strings, which have an special refCount that can never hit zero. + // This means that the static string will never be destroyed, which is important because + // static strings will be shared across threads & ref-counted in a non-threadsafe manner. + StringImpl(const LChar* characters, unsigned length, ConstructStaticStringTag) + : m_refCount(s_refCountFlagIsStaticString) + , m_length(length) + , m_data8(characters) + , m_buffer(0) + , m_hashAndFlags(s_hashFlag8BitBuffer | s_hashFlagIsIdentifier | BufferOwned) + { + // Ensure that the hash is computed so that AtomicStringHash can call existingHash() + // with impunity. The empty string is special because it is never entered into + // AtomicString's HashKey, but still needs to compare correctly. + hash(); + } + + // FIXME: there has to be a less hacky way to do this. + enum Force8Bit { Force8BitConstructor }; + // Create a normal 8-bit string with internal storage (BufferInternal) + StringImpl(unsigned length, Force8Bit) + : m_refCount(s_refCountIncrement) + , m_length(length) + , m_data8(reinterpret_cast<const LChar*>(this + 1)) + , m_buffer(0) + , m_hashAndFlags(s_hashFlag8BitBuffer | BufferInternal) + { + ASSERT(m_data8); + ASSERT(m_length); + } + + // Create a normal 16-bit string with internal storage (BufferInternal) + StringImpl(unsigned length) + : m_refCount(s_refCountIncrement) + , m_length(length) + , m_data16(reinterpret_cast<const UChar*>(this + 1)) + , m_buffer(0) + , m_hashAndFlags(BufferInternal) + { + ASSERT(m_data16); + ASSERT(m_length); + } + + // Create a StringImpl adopting ownership of the provided buffer (BufferOwned) + StringImpl(const LChar* characters, unsigned length) + : m_refCount(s_refCountIncrement) + , m_length(length) + , m_data8(characters) + , m_buffer(0) + , m_hashAndFlags(s_hashFlag8BitBuffer | BufferOwned) + { + ASSERT(m_data8); + ASSERT(m_length); + } + + // Create a StringImpl adopting ownership of the provided buffer (BufferOwned) + StringImpl(const UChar* characters, unsigned length) + : m_refCount(s_refCountIncrement) + , m_length(length) + , m_data16(characters) + , m_buffer(0) + , m_hashAndFlags(BufferOwned) + { + ASSERT(m_data16); + ASSERT(m_length); + } + + // Used to create new strings that are a substring of an existing 8-bit StringImpl (BufferSubstring) + StringImpl(const LChar* characters, unsigned length, PassRefPtr<StringImpl> base) + : m_refCount(s_refCountIncrement) + , m_length(length) + , m_data8(characters) + , m_substringBuffer(base.leakRef()) + , m_hashAndFlags(s_hashFlag8BitBuffer | BufferSubstring) + { + ASSERT(is8Bit()); + ASSERT(m_data8); + ASSERT(m_length); + ASSERT(m_substringBuffer->bufferOwnership() != BufferSubstring); + } + + // Used to create new strings that are a substring of an existing 16-bit StringImpl (BufferSubstring) + StringImpl(const UChar* characters, unsigned length, PassRefPtr<StringImpl> base) + : m_refCount(s_refCountIncrement) + , m_length(length) + , m_data16(characters) + , m_substringBuffer(base.leakRef()) + , m_hashAndFlags(BufferSubstring) + { + ASSERT(!is8Bit()); + ASSERT(m_data16); + ASSERT(m_length); + ASSERT(m_substringBuffer->bufferOwnership() != BufferSubstring); + } + +public: + ~StringImpl(); + + static PassRefPtr<StringImpl> create(const UChar*, unsigned length); + static PassRefPtr<StringImpl> create(const LChar*, unsigned length); + ALWAYS_INLINE static PassRefPtr<StringImpl> create(const char* s, unsigned length) { return create(reinterpret_cast<const LChar*>(s), length); } + static PassRefPtr<StringImpl> create(const LChar*); + ALWAYS_INLINE static PassRefPtr<StringImpl> create(const char* s) { return create(reinterpret_cast<const LChar*>(s)); } + + static ALWAYS_INLINE PassRefPtr<StringImpl> create8(PassRefPtr<StringImpl> rep, unsigned offset, unsigned length) + { + ASSERT(rep); + ASSERT(length <= rep->length()); + + if (!length) + return empty(); + + ASSERT(rep->is8Bit()); + StringImpl* ownerRep = (rep->bufferOwnership() == BufferSubstring) ? rep->m_substringBuffer : rep.get(); + return adoptRef(new StringImpl(rep->m_data8 + offset, length, ownerRep)); + } + + static ALWAYS_INLINE PassRefPtr<StringImpl> create(PassRefPtr<StringImpl> rep, unsigned offset, unsigned length) + { + ASSERT(rep); + ASSERT(length <= rep->length()); + + if (!length) + return empty(); + + StringImpl* ownerRep = (rep->bufferOwnership() == BufferSubstring) ? rep->m_substringBuffer : rep.get(); + if (rep->is8Bit()) + return adoptRef(new StringImpl(rep->m_data8 + offset, length, ownerRep)); + return adoptRef(new StringImpl(rep->m_data16 + offset, length, ownerRep)); + } + + static PassRefPtr<StringImpl> createUninitialized(unsigned length, LChar*& data); + static PassRefPtr<StringImpl> createUninitialized(unsigned length, UChar*& data); + template <typename T> static ALWAYS_INLINE PassRefPtr<StringImpl> tryCreateUninitialized(unsigned length, T*& output) + { + if (!length) { + output = 0; + return empty(); + } + + if (length > ((std::numeric_limits<unsigned>::max() - sizeof(StringImpl)) / sizeof(T))) { + output = 0; + return 0; + } + StringImpl* resultImpl; + if (!tryFastMalloc(sizeof(T) * length + sizeof(StringImpl)).getValue(resultImpl)) { + output = 0; + return 0; + } + output = reinterpret_cast<T*>(resultImpl + 1); + + if (sizeof(T) == sizeof(char)) + return adoptRef(new (NotNull, resultImpl) StringImpl(length, Force8BitConstructor)); + + return adoptRef(new (NotNull, resultImpl) StringImpl(length)); + } + + // Reallocate the StringImpl. The originalString must be only owned by the PassRefPtr, + // and the buffer ownership must be BufferInternal. Just like the input pointer of realloc(), + // the originalString can't be used after this function. + static PassRefPtr<StringImpl> reallocate(PassRefPtr<StringImpl> originalString, unsigned length, LChar*& data); + static PassRefPtr<StringImpl> reallocate(PassRefPtr<StringImpl> originalString, unsigned length, UChar*& data); + + static unsigned flagsOffset() { return OBJECT_OFFSETOF(StringImpl, m_hashAndFlags); } + static unsigned flagIs8Bit() { return s_hashFlag8BitBuffer; } + static unsigned dataOffset() { return OBJECT_OFFSETOF(StringImpl, m_data8); } + static PassRefPtr<StringImpl> createWithTerminatingNullCharacter(const StringImpl&); + + template<typename CharType, size_t inlineCapacity> + static PassRefPtr<StringImpl> adopt(Vector<CharType, inlineCapacity>& vector) + { + if (size_t size = vector.size()) { + ASSERT(vector.data()); + if (size > std::numeric_limits<unsigned>::max()) + CRASH(); + return adoptRef(new StringImpl(vector.releaseBuffer(), size)); + } + return empty(); + } + + static PassRefPtr<StringImpl> adopt(StringBuffer<LChar>& buffer); + static PassRefPtr<StringImpl> adopt(StringBuffer<UChar>& buffer); + + unsigned length() const { return m_length; } + bool is8Bit() const { return m_hashAndFlags & s_hashFlag8BitBuffer; } + + // FIXME: Remove all unnecessary usages of characters() + ALWAYS_INLINE const LChar* characters8() const { ASSERT(is8Bit()); return m_data8; } + ALWAYS_INLINE const UChar* characters16() const { ASSERT(!is8Bit()); return m_data16; } + ALWAYS_INLINE const UChar* characters() const + { + if (!is8Bit()) + return m_data16; + + return getData16SlowCase(); + } + + template <typename CharType> + ALWAYS_INLINE const CharType * getCharacters() const; + + size_t cost() + { + // For substrings, return the cost of the base string. + if (bufferOwnership() == BufferSubstring) + return m_substringBuffer->cost(); + + if (m_hashAndFlags & s_hashFlagDidReportCost) + return 0; + + m_hashAndFlags |= s_hashFlagDidReportCost; + return m_length; + } + + bool has16BitShadow() const { return m_hashAndFlags & s_hashFlagHas16BitShadow; } + void upconvertCharacters(unsigned, unsigned) const; + bool isIdentifier() const { return m_hashAndFlags & s_hashFlagIsIdentifier; } + void setIsIdentifier(bool isIdentifier) + { + ASSERT(!isStatic()); + if (isIdentifier) + m_hashAndFlags |= s_hashFlagIsIdentifier; + else + m_hashAndFlags &= ~s_hashFlagIsIdentifier; + } + + bool hasTerminatingNullCharacter() const { return m_hashAndFlags & s_hashFlagHasTerminatingNullCharacter; } + + bool isAtomic() const { return m_hashAndFlags & s_hashFlagIsAtomic; } + void setIsAtomic(bool isIdentifier) + { + ASSERT(!isStatic()); + if (isIdentifier) + m_hashAndFlags |= s_hashFlagIsAtomic; + else + m_hashAndFlags &= ~s_hashFlagIsAtomic; + } + +private: + // The high bits of 'hash' are always empty, but we prefer to store our flags + // in the low bits because it makes them slightly more efficient to access. + // So, we shift left and right when setting and getting our hash code. + void setHash(unsigned hash) const + { + ASSERT(!hasHash()); + // Multiple clients assume that StringHasher is the canonical string hash function. + ASSERT(hash == (is8Bit() ? StringHasher::computeHash(m_data8, m_length) : StringHasher::computeHash(m_data16, m_length))); + ASSERT(!(hash & (s_flagMask << (8 * sizeof(hash) - s_flagCount)))); // Verify that enough high bits are empty. + + hash <<= s_flagCount; + ASSERT(!(hash & m_hashAndFlags)); // Verify that enough low bits are empty after shift. + ASSERT(hash); // Verify that 0 is a valid sentinel hash value. + + m_hashAndFlags |= hash; // Store hash with flags in low bits. + } + + unsigned rawHash() const + { + return m_hashAndFlags >> s_flagCount; + } + +public: + bool hasHash() const + { + return rawHash() != 0; + } + + unsigned existingHash() const + { + ASSERT(hasHash()); + return rawHash(); + } + + unsigned hash() const + { + if (hasHash()) + return existingHash(); + return hashSlowCase(); + } + + inline bool hasOneRef() const + { + return m_refCount == s_refCountIncrement; + } + + inline void ref() + { + m_refCount += s_refCountIncrement; + } + + inline void deref() + { + if (m_refCount == s_refCountIncrement) { + delete this; + return; + } + + m_refCount -= s_refCountIncrement; + } + + static StringImpl* empty(); + + // FIXME: Does this really belong in StringImpl? + template <typename T> static void copyChars(T* destination, const T* source, unsigned numCharacters) + { + if (numCharacters == 1) { + *destination = *source; + return; + } + + if (numCharacters <= s_copyCharsInlineCutOff) { + unsigned i = 0; +#if (CPU(X86) || CPU(X86_64)) + const unsigned charsPerInt = sizeof(uint32_t) / sizeof(T); + + if (numCharacters > charsPerInt) { + unsigned stopCount = numCharacters & ~(charsPerInt - 1); + + const uint32_t* srcCharacters = reinterpret_cast<const uint32_t*>(source); + uint32_t* destCharacters = reinterpret_cast<uint32_t*>(destination); + for (unsigned j = 0; i < stopCount; i += charsPerInt, ++j) + destCharacters[j] = srcCharacters[j]; + } +#endif + for (; i < numCharacters; ++i) + destination[i] = source[i]; + } else + memcpy(destination, source, numCharacters * sizeof(T)); + } + + // Some string features, like refcounting and the atomicity flag, are not + // thread-safe. We achieve thread safety by isolation, giving each thread + // its own copy of the string. + PassRefPtr<StringImpl> isolatedCopy() const; + + PassRefPtr<StringImpl> substring(unsigned pos, unsigned len = UINT_MAX); + + UChar operator[](unsigned i) const + { + ASSERT(i < m_length); + if (is8Bit()) + return m_data8[i]; + return m_data16[i]; + } + UChar32 characterStartingAt(unsigned); + + bool containsOnlyWhitespace(); + + int toIntStrict(bool* ok = 0, int base = 10); + unsigned toUIntStrict(bool* ok = 0, int base = 10); + int64_t toInt64Strict(bool* ok = 0, int base = 10); + uint64_t toUInt64Strict(bool* ok = 0, int base = 10); + intptr_t toIntPtrStrict(bool* ok = 0, int base = 10); + + int toInt(bool* ok = 0); // ignores trailing garbage + unsigned toUInt(bool* ok = 0); // ignores trailing garbage + int64_t toInt64(bool* ok = 0); // ignores trailing garbage + uint64_t toUInt64(bool* ok = 0); // ignores trailing garbage + intptr_t toIntPtr(bool* ok = 0); // ignores trailing garbage + + double toDouble(bool* ok = 0, bool* didReadNumber = 0); + float toFloat(bool* ok = 0, bool* didReadNumber = 0); + + PassRefPtr<StringImpl> lower(); + PassRefPtr<StringImpl> upper(); + + PassRefPtr<StringImpl> fill(UChar); + // FIXME: Do we need fill(char) or can we just do the right thing if UChar is ASCII? + PassRefPtr<StringImpl> foldCase(); + + PassRefPtr<StringImpl> stripWhiteSpace(); + PassRefPtr<StringImpl> stripWhiteSpace(IsWhiteSpaceFunctionPtr); + PassRefPtr<StringImpl> simplifyWhiteSpace(); + PassRefPtr<StringImpl> simplifyWhiteSpace(IsWhiteSpaceFunctionPtr); + + PassRefPtr<StringImpl> removeCharacters(CharacterMatchFunctionPtr); + template <typename CharType> + ALWAYS_INLINE PassRefPtr<StringImpl> removeCharacters(const CharType* characters, CharacterMatchFunctionPtr); + + size_t find(UChar, unsigned index = 0); + size_t find(CharacterMatchFunctionPtr, unsigned index = 0); + size_t find(const LChar*, unsigned index = 0); + ALWAYS_INLINE size_t find(const char* s, unsigned index = 0) { return find(reinterpret_cast<const LChar*>(s), index); }; + size_t find(StringImpl*, unsigned index = 0); + size_t findIgnoringCase(const LChar*, unsigned index = 0); + ALWAYS_INLINE size_t findIgnoringCase(const char* s, unsigned index = 0) { return findIgnoringCase(reinterpret_cast<const LChar*>(s), index); }; + size_t findIgnoringCase(StringImpl*, unsigned index = 0); + + size_t reverseFind(UChar, unsigned index = UINT_MAX); + size_t reverseFind(StringImpl*, unsigned index = UINT_MAX); + size_t reverseFindIgnoringCase(StringImpl*, unsigned index = UINT_MAX); + + bool startsWith(StringImpl* str, bool caseSensitive = true) { return (caseSensitive ? reverseFind(str, 0) : reverseFindIgnoringCase(str, 0)) == 0; } + bool endsWith(StringImpl*, bool caseSensitive = true); + + PassRefPtr<StringImpl> replace(UChar, UChar); + PassRefPtr<StringImpl> replace(UChar, StringImpl*); + PassRefPtr<StringImpl> replace(StringImpl*, StringImpl*); + PassRefPtr<StringImpl> replace(unsigned index, unsigned len, StringImpl*); + + WTF::Unicode::Direction defaultWritingDirection(bool* hasStrongDirectionality = 0); + +#if USE(CF) + CFStringRef createCFString(); +#endif +#ifdef __OBJC__ + operator NSString*(); +#endif + +private: + // This number must be at least 2 to avoid sharing empty, null as well as 1 character strings from SmallStrings. + static const unsigned s_copyCharsInlineCutOff = 20; + + BufferOwnership bufferOwnership() const { return static_cast<BufferOwnership>(m_hashAndFlags & s_hashMaskBufferOwnership); } + bool isStatic() const { return m_refCount & s_refCountFlagIsStaticString; } + template <class UCharPredicate> PassRefPtr<StringImpl> stripMatchedCharacters(UCharPredicate); + template <typename CharType, class UCharPredicate> PassRefPtr<StringImpl> simplifyMatchedCharactersToSpace(UCharPredicate); + NEVER_INLINE const UChar* getData16SlowCase() const; + NEVER_INLINE unsigned hashSlowCase() const; + + // The bottom bit in the ref count indicates a static (immortal) string. + static const unsigned s_refCountFlagIsStaticString = 0x1; + static const unsigned s_refCountIncrement = 0x2; // This allows us to ref / deref without disturbing the static string flag. + + // The bottom 8 bits in the hash are flags. + static const unsigned s_flagCount = 8; + static const unsigned s_flagMask = (1u << s_flagCount) - 1; + COMPILE_ASSERT(s_flagCount == StringHasher::flagCount, StringHasher_reserves_enough_bits_for_StringImpl_flags); + + static const unsigned s_hashFlagHas16BitShadow = 1u << 7; + static const unsigned s_hashFlag8BitBuffer = 1u << 6; + static const unsigned s_hashFlagHasTerminatingNullCharacter = 1u << 5; + static const unsigned s_hashFlagIsAtomic = 1u << 4; + static const unsigned s_hashFlagDidReportCost = 1u << 3; + static const unsigned s_hashFlagIsIdentifier = 1u << 2; + static const unsigned s_hashMaskBufferOwnership = 1u | (1u << 1); + + unsigned m_refCount; + unsigned m_length; + union { + const LChar* m_data8; + const UChar* m_data16; + }; + union { + void* m_buffer; + StringImpl* m_substringBuffer; + mutable UChar* m_copyData16; + }; + mutable unsigned m_hashAndFlags; +}; + +template <> +ALWAYS_INLINE const LChar* StringImpl::getCharacters<LChar>() const { return characters8(); } + +template <> +ALWAYS_INLINE const UChar* StringImpl::getCharacters<UChar>() const { return characters16(); } + +bool equal(const StringImpl*, const StringImpl*); +bool equal(const StringImpl*, const LChar*); +inline bool equal(const StringImpl* a, const char* b) { return equal(a, reinterpret_cast<const LChar*>(b)); } +bool equal(const StringImpl*, const LChar*, unsigned); +inline bool equal(const StringImpl* a, const char* b, unsigned length) { return equal(a, reinterpret_cast<const LChar*>(b), length); } +inline bool equal(const LChar* a, StringImpl* b) { return equal(b, a); } +inline bool equal(const char* a, StringImpl* b) { return equal(b, reinterpret_cast<const LChar*>(a)); } +bool equal(const StringImpl*, const UChar*, unsigned); + +// Do comparisons 8 or 4 bytes-at-a-time on architectures where it's safe. +#if CPU(X86_64) +ALWAYS_INLINE bool equal(const LChar* a, const LChar* b, unsigned length) +{ + unsigned dwordLength = length >> 3; + + if (dwordLength) { + const uint64_t* aDWordCharacters = reinterpret_cast<const uint64_t*>(a); + const uint64_t* bDWordCharacters = reinterpret_cast<const uint64_t*>(b); + + for (unsigned i = 0; i != dwordLength; ++i) { + if (*aDWordCharacters++ != *bDWordCharacters++) + return false; + } + + a = reinterpret_cast<const LChar*>(aDWordCharacters); + b = reinterpret_cast<const LChar*>(bDWordCharacters); + } + + if (length & 4) { + if (*reinterpret_cast<const uint32_t*>(a) != *reinterpret_cast<const uint32_t*>(b)) + return false; + + a += 4; + b += 4; + } + + if (length & 2) { + if (*reinterpret_cast<const uint16_t*>(a) != *reinterpret_cast<const uint16_t*>(b)) + return false; + + a += 2; + b += 2; + } + + if (length & 1 && (*a != *b)) + return false; + + return true; +} + +ALWAYS_INLINE bool equal(const UChar* a, const UChar* b, unsigned length) +{ + unsigned dwordLength = length >> 2; + + if (dwordLength) { + const uint64_t* aDWordCharacters = reinterpret_cast<const uint64_t*>(a); + const uint64_t* bDWordCharacters = reinterpret_cast<const uint64_t*>(b); + + for (unsigned i = 0; i != dwordLength; ++i) { + if (*aDWordCharacters++ != *bDWordCharacters++) + return false; + } + + a = reinterpret_cast<const UChar*>(aDWordCharacters); + b = reinterpret_cast<const UChar*>(bDWordCharacters); + } + + if (length & 2) { + if (*reinterpret_cast<const uint32_t*>(a) != *reinterpret_cast<const uint32_t*>(b)) + return false; + + a += 2; + b += 2; + } + + if (length & 1 && (*a != *b)) + return false; + + return true; +} +#elif CPU(X86) +ALWAYS_INLINE bool equal(const LChar* a, const LChar* b, unsigned length) +{ + const uint32_t* aCharacters = reinterpret_cast<const uint32_t*>(a); + const uint32_t* bCharacters = reinterpret_cast<const uint32_t*>(b); + + unsigned wordLength = length >> 2; + for (unsigned i = 0; i != wordLength; ++i) { + if (*aCharacters++ != *bCharacters++) + return false; + } + + length &= 3; + + if (length) { + const LChar* aRemainder = reinterpret_cast<const LChar*>(aCharacters); + const LChar* bRemainder = reinterpret_cast<const LChar*>(bCharacters); + + for (unsigned i = 0; i < length; ++i) { + if (aRemainder[i] != bRemainder[i]) + return false; + } + } + + return true; +} + +ALWAYS_INLINE bool equal(const UChar* a, const UChar* b, unsigned length) +{ + const uint32_t* aCharacters = reinterpret_cast<const uint32_t*>(a); + const uint32_t* bCharacters = reinterpret_cast<const uint32_t*>(b); + + unsigned wordLength = length >> 1; + for (unsigned i = 0; i != wordLength; ++i) { + if (*aCharacters++ != *bCharacters++) + return false; + } + + if (length & 1 && *reinterpret_cast<const UChar*>(aCharacters) != *reinterpret_cast<const UChar*>(bCharacters)) + return false; + + return true; +} +#else +ALWAYS_INLINE bool equal(const LChar* a, const LChar* b, unsigned length) +{ + for (unsigned i = 0; i != length; ++i) { + if (a[i] != b[i]) + return false; + } + + return true; +} + +ALWAYS_INLINE bool equal(const UChar* a, const UChar* b, unsigned length) +{ + for (unsigned i = 0; i != length; ++i) { + if (a[i] != b[i]) + return false; + } + + return true; +} +#endif + +ALWAYS_INLINE bool equal(const LChar* a, const UChar* b, unsigned length) +{ + for (unsigned i = 0; i != length; ++i) { + if (a[i] != b[i]) + return false; + } + + return true; +} + +ALWAYS_INLINE bool equal(const UChar* a, const LChar* b, unsigned length) +{ + for (unsigned i = 0; i != length; ++i) { + if (a[i] != b[i]) + return false; + } + + return true; +} + +bool equalIgnoringCase(StringImpl*, StringImpl*); +bool equalIgnoringCase(StringImpl*, const LChar*); +inline bool equalIgnoringCase(const LChar* a, StringImpl* b) { return equalIgnoringCase(b, a); } +bool equalIgnoringCase(const UChar*, const LChar*, unsigned); +inline bool equalIgnoringCase(const UChar* a, const char* b, unsigned length) { return equalIgnoringCase(a, reinterpret_cast<const LChar*>(b), length); } +inline bool equalIgnoringCase(const LChar* a, const UChar* b, unsigned length) { return equalIgnoringCase(b, a, length); } +inline bool equalIgnoringCase(const char* a, const UChar* b, unsigned length) { return equalIgnoringCase(b, reinterpret_cast<const LChar*>(a), length); } + +bool equalIgnoringNullity(StringImpl*, StringImpl*); + +template<size_t inlineCapacity> +bool equalIgnoringNullity(const Vector<UChar, inlineCapacity>& a, StringImpl* b) +{ + if (!b) + return !a.size(); + if (a.size() != b->length()) + return false; + return !memcmp(a.data(), b->characters(), b->length()); +} + +int codePointCompare(const StringImpl*, const StringImpl*); + +static inline bool isSpaceOrNewline(UChar c) +{ + // Use isASCIISpace() for basic Latin-1. + // This will include newlines, which aren't included in Unicode DirWS. + return c <= 0x7F ? WTF::isASCIISpace(c) : WTF::Unicode::direction(c) == WTF::Unicode::WhiteSpaceNeutral; +} + +inline PassRefPtr<StringImpl> StringImpl::isolatedCopy() const +{ + if (is8Bit()) + return create(m_data8, m_length); + return create(m_data16, m_length); +} + +struct StringHash; + +// StringHash is the default hash for StringImpl* and RefPtr<StringImpl> +template<typename T> struct DefaultHash; +template<> struct DefaultHash<StringImpl*> { + typedef StringHash Hash; +}; +template<> struct DefaultHash<RefPtr<StringImpl> > { + typedef StringHash Hash; +}; + +} + +using WTF::StringImpl; +using WTF::equal; +using WTF::TextCaseSensitivity; +using WTF::TextCaseSensitive; +using WTF::TextCaseInsensitive; + +#endif diff --git a/Source/JavaScriptCore/wtf/text/StringOperators.h b/Source/JavaScriptCore/wtf/text/StringOperators.h new file mode 100644 index 000000000..9e1637be1 --- /dev/null +++ b/Source/JavaScriptCore/wtf/text/StringOperators.h @@ -0,0 +1,150 @@ +/* + * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010 Apple Inc. All rights reserved. + * Copyright (C) Research In Motion Limited 2011. All rights reserved. + * + * This library 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 library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#ifndef StringOperators_h +#define StringOperators_h + +namespace WTF { + +template<typename StringType1, typename StringType2> +class StringAppend { +public: + StringAppend(StringType1 string1, StringType2 string2) + : m_string1(string1) + , m_string2(string2) + { + } + + operator String() const + { + RefPtr<StringImpl> resultImpl = tryMakeString(m_string1, m_string2); + if (!resultImpl) + CRASH(); + return resultImpl.release(); + } + + operator AtomicString() const + { + return operator String(); + } + + bool is8Bit() + { + StringTypeAdapter<StringType1> adapter1(m_string1); + StringTypeAdapter<StringType2> adapter2(m_string2); + return adapter1.is8Bit() && adapter2.is8Bit(); + } + + void writeTo(LChar* destination) + { + ASSERT(is8Bit()); + StringTypeAdapter<StringType1> adapter1(m_string1); + StringTypeAdapter<StringType2> adapter2(m_string2); + adapter1.writeTo(destination); + adapter2.writeTo(destination + adapter1.length()); + } + + void writeTo(UChar* destination) + { + StringTypeAdapter<StringType1> adapter1(m_string1); + StringTypeAdapter<StringType2> adapter2(m_string2); + adapter1.writeTo(destination); + adapter2.writeTo(destination + adapter1.length()); + } + + unsigned length() + { + StringTypeAdapter<StringType1> adapter1(m_string1); + StringTypeAdapter<StringType2> adapter2(m_string2); + return adapter1.length() + adapter2.length(); + } + +private: + StringType1 m_string1; + StringType2 m_string2; +}; + +template<typename StringType1, typename StringType2> +class StringTypeAdapter<StringAppend<StringType1, StringType2> > { +public: + StringTypeAdapter<StringAppend<StringType1, StringType2> >(StringAppend<StringType1, StringType2>& buffer) + : m_buffer(buffer) + { + } + + unsigned length() { return m_buffer.length(); } + + bool is8Bit() { return m_buffer.is8Bit(); } + + void writeTo(LChar* destination) { m_buffer.writeTo(destination); } + void writeTo(UChar* destination) { m_buffer.writeTo(destination); } + +private: + StringAppend<StringType1, StringType2>& m_buffer; +}; + +inline StringAppend<const char*, String> operator+(const char* string1, const String& string2) +{ + return StringAppend<const char*, String>(string1, string2); +} + +inline StringAppend<const char*, AtomicString> operator+(const char* string1, const AtomicString& string2) +{ + return StringAppend<const char*, AtomicString>(string1, string2); +} + +template<typename U, typename V> +StringAppend<const char*, StringAppend<U, V> > operator+(const char* string1, const StringAppend<U, V>& string2) +{ + return StringAppend<const char*, StringAppend<U, V> >(string1, string2); +} + +inline StringAppend<const UChar*, String> operator+(const UChar* string1, const String& string2) +{ + return StringAppend<const UChar*, String>(string1, string2); +} + +inline StringAppend<const UChar*, AtomicString> operator+(const UChar* string1, const AtomicString& string2) +{ + return StringAppend<const UChar*, AtomicString>(string1, string2); +} + +template<typename U, typename V> +StringAppend<const UChar*, StringAppend<U, V> > operator+(const UChar* string1, const StringAppend<U, V>& string2) +{ + return StringAppend<const UChar*, StringAppend<U, V> >(string1, string2); +} + +template<typename T> +StringAppend<String, T> operator+(const String& string1, T string2) +{ + return StringAppend<String, T>(string1, string2); +} + +template<typename U, typename V, typename W> +StringAppend<StringAppend<U, V>, W> operator+(const StringAppend<U, V>& string1, W string2) +{ + return StringAppend<StringAppend<U, V>, W>(string1, string2); +} + +} // namespace WTF + +#endif // StringOperators_h diff --git a/Source/JavaScriptCore/wtf/text/StringStatics.cpp b/Source/JavaScriptCore/wtf/text/StringStatics.cpp new file mode 100644 index 000000000..1a80f6d48 --- /dev/null +++ b/Source/JavaScriptCore/wtf/text/StringStatics.cpp @@ -0,0 +1,92 @@ +/* + * Copyright (C) 2010 Apple Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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 APPLE INC. ``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 APPLE INC. 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 "config.h" + +#ifdef SKIP_STATIC_CONSTRUCTORS_ON_GCC +#define ATOMICSTRING_HIDE_GLOBALS 1 +#endif + +#include "AtomicString.h" +#include "DynamicAnnotations.h" +#include "MainThread.h" +#include "StaticConstructors.h" +#include "StringImpl.h" + +namespace WTF { + +StringImpl* StringImpl::empty() +{ + // FIXME: This works around a bug in our port of PCRE, that a regular expression + // run on the empty string may still perform a read from the first element, and + // as such we need this to be a valid pointer. No code should ever be reading + // from a zero length string, so this should be able to be a non-null pointer + // into the zero-page. + // Replace this with 'reinterpret_cast<UChar*>(static_cast<intptr_t>(1))' once + // PCRE goes away. + static LChar emptyLCharData = 0; + DEFINE_STATIC_LOCAL(StringImpl, emptyString, (&emptyLCharData, 0, ConstructStaticString)); + WTF_ANNOTATE_BENIGN_RACE(&emptyString, "Benign race on StringImpl::emptyString reference counter"); + return &emptyString; +} + +WTF_EXPORTDATA DEFINE_GLOBAL(AtomicString, nullAtom) +WTF_EXPORTDATA DEFINE_GLOBAL(AtomicString, emptyAtom, "") +WTF_EXPORTDATA DEFINE_GLOBAL(AtomicString, textAtom, "#text") +WTF_EXPORTDATA DEFINE_GLOBAL(AtomicString, commentAtom, "#comment") +WTF_EXPORTDATA DEFINE_GLOBAL(AtomicString, starAtom, "*") +WTF_EXPORTDATA DEFINE_GLOBAL(AtomicString, xmlAtom, "xml") +WTF_EXPORTDATA DEFINE_GLOBAL(AtomicString, xmlnsAtom, "xmlns") + +NEVER_INLINE unsigned StringImpl::hashSlowCase() const +{ + if (is8Bit()) + setHash(StringHasher::computeHash(m_data8, m_length)); + else + setHash(StringHasher::computeHash(m_data16, m_length)); + return existingHash(); +} + +void AtomicString::init() +{ + static bool initialized; + if (!initialized) { + // Initialization is not thread safe, so this function must be called from the main thread first. + ASSERT(isMainThread()); + + // Use placement new to initialize the globals. + new (NotNull, (void*)&nullAtom) AtomicString; + new (NotNull, (void*)&emptyAtom) AtomicString(""); + new (NotNull, (void*)&textAtom) AtomicString("#text"); + new (NotNull, (void*)&commentAtom) AtomicString("#comment"); + new (NotNull, (void*)&starAtom) AtomicString("*"); + new (NotNull, (void*)&xmlAtom) AtomicString("xml"); + new (NotNull, (void*)&xmlnsAtom) AtomicString("xmlns"); + + initialized = true; + } +} + +} diff --git a/Source/JavaScriptCore/wtf/text/TextPosition.h b/Source/JavaScriptCore/wtf/text/TextPosition.h new file mode 100644 index 000000000..be49c157a --- /dev/null +++ b/Source/JavaScriptCore/wtf/text/TextPosition.h @@ -0,0 +1,85 @@ +/* + * Copyright (C) 2010, Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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 APPLE INC. AND ITS 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 APPLE INC. OR ITS 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. + */ + +#ifndef TextPosition_h +#define TextPosition_h + +#include <wtf/Assertions.h> + +namespace WTF { + +// An abstract number of element in a sequence. The sequence has a first element. +// This type should be used instead of integer because 2 contradicting traditions can +// call a first element '0' or '1' which makes integer type ambiguous. +class OrdinalNumber { +public: + static OrdinalNumber fromZeroBasedInt(int zeroBasedInt) { return OrdinalNumber(zeroBasedInt); } + static OrdinalNumber fromOneBasedInt(int oneBasedInt) { return OrdinalNumber(oneBasedInt - 1); } + OrdinalNumber() : m_zeroBasedValue(0) { } + + int zeroBasedInt() const { return m_zeroBasedValue; } + int oneBasedInt() const { return m_zeroBasedValue + 1; } + + bool operator==(OrdinalNumber other) { return m_zeroBasedValue == other.m_zeroBasedValue; } + bool operator!=(OrdinalNumber other) { return !((*this) == other); } + + static OrdinalNumber first() { return OrdinalNumber(0); } + static OrdinalNumber beforeFirst() { return OrdinalNumber(-1); } + +private: + OrdinalNumber(int zeroBasedInt) : m_zeroBasedValue(zeroBasedInt) { } + int m_zeroBasedValue; +}; + + +// TextPosition structure specifies coordinates within an text resource. It is used mostly +// for saving script source position. +class TextPosition { +public: + TextPosition(OrdinalNumber line, OrdinalNumber column) + : m_line(line) + , m_column(column) + { + } + TextPosition() { } + bool operator==(const TextPosition& other) { return m_line == other.m_line && m_column == other.m_column; } + bool operator!=(const TextPosition& other) { return !((*this) == other); } + + // A 'minimum' value of position, used as a default value. + static TextPosition minimumPosition() { return TextPosition(OrdinalNumber::first(), OrdinalNumber::first()); } + + // A value with line value less than a minimum; used as an impossible position. + static TextPosition belowRangePosition() { return TextPosition(OrdinalNumber::beforeFirst(), OrdinalNumber::beforeFirst()); } + + OrdinalNumber m_line; + OrdinalNumber m_column; +}; + +} + +using WTF::OrdinalNumber; + +using WTF::TextPosition; + +#endif // TextPosition_h diff --git a/Source/JavaScriptCore/wtf/text/WTFString.cpp b/Source/JavaScriptCore/wtf/text/WTFString.cpp new file mode 100644 index 000000000..4c42ed6a2 --- /dev/null +++ b/Source/JavaScriptCore/wtf/text/WTFString.cpp @@ -0,0 +1,1126 @@ +/* + * (C) 1999 Lars Knoll (knoll@kde.org) + * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2010 Apple Inc. All rights reserved. + * Copyright (C) 2007-2009 Torch Mobile, Inc. + * + * This library 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 library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "config.h" +#include "WTFString.h" + +#include <stdarg.h> +#include <wtf/ASCIICType.h> +#include <wtf/text/CString.h> +#include <wtf/StringExtras.h> +#include <wtf/Vector.h> +#include <wtf/dtoa.h> +#include <wtf/unicode/UTF8.h> +#include <wtf/unicode/Unicode.h> + +using namespace std; + +namespace WTF { + +using namespace Unicode; +using namespace std; + +// Construct a string with UTF-16 data. +String::String(const UChar* characters, unsigned length) + : m_impl(characters ? StringImpl::create(characters, length) : 0) +{ +} + +// Construct a string with UTF-16 data, from a null-terminated source. +String::String(const UChar* str) +{ + if (!str) + return; + + size_t len = 0; + while (str[len] != UChar(0)) + len++; + + if (len > numeric_limits<unsigned>::max()) + CRASH(); + + m_impl = StringImpl::create(str, len); +} + +// Construct a string with latin1 data. +String::String(const LChar* characters, unsigned length) + : m_impl(characters ? StringImpl::create(characters, length) : 0) +{ +} + +String::String(const char* characters, unsigned length) + : m_impl(characters ? StringImpl::create(reinterpret_cast<const LChar*>(characters), length) : 0) +{ +} + +// Construct a string with latin1 data, from a null-terminated source. +String::String(const LChar* characters) + : m_impl(characters ? StringImpl::create(characters) : 0) +{ +} + +String::String(const char* characters) + : m_impl(characters ? StringImpl::create(reinterpret_cast<const LChar*>(characters)) : 0) +{ +} + +void String::append(const String& str) +{ + if (str.isEmpty()) + return; + + // FIXME: This is extremely inefficient. So much so that we might want to take this + // out of String's API. We can make it better by optimizing the case where exactly + // one String is pointing at this StringImpl, but even then it's going to require a + // call to fastMalloc every single time. + if (str.m_impl) { + if (m_impl) { + UChar* data; + if (str.length() > numeric_limits<unsigned>::max() - m_impl->length()) + CRASH(); + RefPtr<StringImpl> newImpl = StringImpl::createUninitialized(m_impl->length() + str.length(), data); + memcpy(data, m_impl->characters(), m_impl->length() * sizeof(UChar)); + memcpy(data + m_impl->length(), str.characters(), str.length() * sizeof(UChar)); + m_impl = newImpl.release(); + } else + m_impl = str.m_impl; + } +} + +void String::append(LChar c) +{ + // FIXME: This is extremely inefficient. So much so that we might want to take this + // out of String's API. We can make it better by optimizing the case where exactly + // one String is pointing at this StringImpl, but even then it's going to require a + // call to fastMalloc every single time. + if (m_impl) { + UChar* data; + if (m_impl->length() >= numeric_limits<unsigned>::max()) + CRASH(); + RefPtr<StringImpl> newImpl = StringImpl::createUninitialized(m_impl->length() + 1, data); + memcpy(data, m_impl->characters(), m_impl->length() * sizeof(UChar)); + data[m_impl->length()] = c; + m_impl = newImpl.release(); + } else + m_impl = StringImpl::create(&c, 1); +} + +void String::append(UChar c) +{ + // FIXME: This is extremely inefficient. So much so that we might want to take this + // out of String's API. We can make it better by optimizing the case where exactly + // one String is pointing at this StringImpl, but even then it's going to require a + // call to fastMalloc every single time. + if (m_impl) { + UChar* data; + if (m_impl->length() >= numeric_limits<unsigned>::max()) + CRASH(); + RefPtr<StringImpl> newImpl = StringImpl::createUninitialized(m_impl->length() + 1, data); + memcpy(data, m_impl->characters(), m_impl->length() * sizeof(UChar)); + data[m_impl->length()] = c; + m_impl = newImpl.release(); + } else + m_impl = StringImpl::create(&c, 1); +} + +int codePointCompare(const String& a, const String& b) +{ + return codePointCompare(a.impl(), b.impl()); +} + +void String::insert(const String& str, unsigned pos) +{ + if (str.isEmpty()) { + if (str.isNull()) + return; + if (isNull()) + m_impl = str.impl(); + return; + } + insert(str.characters(), str.length(), pos); +} + +void String::append(const UChar* charactersToAppend, unsigned lengthToAppend) +{ + if (!m_impl) { + if (!charactersToAppend) + return; + m_impl = StringImpl::create(charactersToAppend, lengthToAppend); + return; + } + + if (!lengthToAppend) + return; + + ASSERT(charactersToAppend); + UChar* data; + if (lengthToAppend > numeric_limits<unsigned>::max() - length()) + CRASH(); + RefPtr<StringImpl> newImpl = StringImpl::createUninitialized(length() + lengthToAppend, data); + memcpy(data, characters(), length() * sizeof(UChar)); + memcpy(data + length(), charactersToAppend, lengthToAppend * sizeof(UChar)); + m_impl = newImpl.release(); +} + +void String::insert(const UChar* charactersToInsert, unsigned lengthToInsert, unsigned position) +{ + if (position >= length()) { + append(charactersToInsert, lengthToInsert); + return; + } + + ASSERT(m_impl); + + if (!lengthToInsert) + return; + + ASSERT(charactersToInsert); + UChar* data; + if (lengthToInsert > numeric_limits<unsigned>::max() - length()) + CRASH(); + RefPtr<StringImpl> newImpl = StringImpl::createUninitialized(length() + lengthToInsert, data); + memcpy(data, characters(), position * sizeof(UChar)); + memcpy(data + position, charactersToInsert, lengthToInsert * sizeof(UChar)); + memcpy(data + position + lengthToInsert, characters() + position, (length() - position) * sizeof(UChar)); + m_impl = newImpl.release(); +} + +UChar32 String::characterStartingAt(unsigned i) const +{ + if (!m_impl || i >= m_impl->length()) + return 0; + return m_impl->characterStartingAt(i); +} + +void String::truncate(unsigned position) +{ + if (position >= length()) + return; + UChar* data; + RefPtr<StringImpl> newImpl = StringImpl::createUninitialized(position, data); + memcpy(data, characters(), position * sizeof(UChar)); + m_impl = newImpl.release(); +} + +void String::remove(unsigned position, int lengthToRemove) +{ + if (lengthToRemove <= 0) + return; + if (position >= length()) + return; + if (static_cast<unsigned>(lengthToRemove) > length() - position) + lengthToRemove = length() - position; + UChar* data; + RefPtr<StringImpl> newImpl = StringImpl::createUninitialized(length() - lengthToRemove, data); + memcpy(data, characters(), position * sizeof(UChar)); + memcpy(data + position, characters() + position + lengthToRemove, + (length() - lengthToRemove - position) * sizeof(UChar)); + m_impl = newImpl.release(); +} + +String String::substring(unsigned pos, unsigned len) const +{ + if (!m_impl) + return String(); + return m_impl->substring(pos, len); +} + +String String::substringSharingImpl(unsigned offset, unsigned length) const +{ + // FIXME: We used to check against a limit of Heap::minExtraCost / sizeof(UChar). + + unsigned stringLength = this->length(); + offset = min(offset, stringLength); + length = min(length, stringLength - offset); + + if (!offset && length == stringLength) + return *this; + return String(StringImpl::create(m_impl, offset, length)); +} + +String String::lower() const +{ + if (!m_impl) + return String(); + return m_impl->lower(); +} + +String String::upper() const +{ + if (!m_impl) + return String(); + return m_impl->upper(); +} + +String String::stripWhiteSpace() const +{ + if (!m_impl) + return String(); + return m_impl->stripWhiteSpace(); +} + +String String::stripWhiteSpace(IsWhiteSpaceFunctionPtr isWhiteSpace) const +{ + if (!m_impl) + return String(); + return m_impl->stripWhiteSpace(isWhiteSpace); +} + +String String::simplifyWhiteSpace() const +{ + if (!m_impl) + return String(); + return m_impl->simplifyWhiteSpace(); +} + +String String::simplifyWhiteSpace(IsWhiteSpaceFunctionPtr isWhiteSpace) const +{ + if (!m_impl) + return String(); + return m_impl->simplifyWhiteSpace(isWhiteSpace); +} + +String String::removeCharacters(CharacterMatchFunctionPtr findMatch) const +{ + if (!m_impl) + return String(); + return m_impl->removeCharacters(findMatch); +} + +String String::foldCase() const +{ + if (!m_impl) + return String(); + return m_impl->foldCase(); +} + +bool String::percentage(int& result) const +{ + if (!m_impl || !m_impl->length()) + return false; + + if ((*m_impl)[m_impl->length() - 1] != '%') + return false; + + result = charactersToIntStrict(m_impl->characters(), m_impl->length() - 1); + return true; +} + +const UChar* String::charactersWithNullTermination() +{ + if (!m_impl) + return 0; + if (m_impl->hasTerminatingNullCharacter()) + return m_impl->characters(); + m_impl = StringImpl::createWithTerminatingNullCharacter(*m_impl); + return m_impl->characters(); +} + +String String::format(const char *format, ...) +{ +#if PLATFORM(QT) + // Use QString::vsprintf to avoid the locale dependent formatting of vsnprintf. + // https://bugs.webkit.org/show_bug.cgi?id=18994 + va_list args; + va_start(args, format); + + QString buffer; + buffer.vsprintf(format, args); + + va_end(args); + + QByteArray ba = buffer.toUtf8(); + return StringImpl::create(reinterpret_cast<const LChar*>(ba.constData()), ba.length()); + +#elif OS(WINCE) + va_list args; + va_start(args, format); + + Vector<char, 256> buffer; + + int bufferSize = 256; + buffer.resize(bufferSize); + for (;;) { + int written = vsnprintf(buffer.data(), bufferSize, format, args); + va_end(args); + + if (written == 0) + return String(""); + if (written > 0) + return StringImpl::create(reinterpret_cast<const LChar*>(buffer.data()), written); + + bufferSize <<= 1; + buffer.resize(bufferSize); + va_start(args, format); + } + +#else + va_list args; + va_start(args, format); + + Vector<char, 256> buffer; + + // Do the format once to get the length. +#if COMPILER(MSVC) + int result = _vscprintf(format, args); +#else + char ch; + int result = vsnprintf(&ch, 1, format, args); + // We need to call va_end() and then va_start() again here, as the + // contents of args is undefined after the call to vsnprintf + // according to http://man.cx/snprintf(3) + // + // Not calling va_end/va_start here happens to work on lots of + // systems, but fails e.g. on 64bit Linux. + va_end(args); + va_start(args, format); +#endif + + if (result == 0) + return String(""); + if (result < 0) + return String(); + unsigned len = result; + buffer.grow(len + 1); + + // Now do the formatting again, guaranteed to fit. + vsnprintf(buffer.data(), buffer.size(), format, args); + + va_end(args); + + return StringImpl::create(reinterpret_cast<const LChar*>(buffer.data()), len); +#endif +} + +String String::number(short n) +{ + return String::format("%hd", n); +} + +String String::number(unsigned short n) +{ + return String::format("%hu", n); +} + +String String::number(int n) +{ + return String::format("%d", n); +} + +String String::number(unsigned n) +{ + return String::format("%u", n); +} + +String String::number(long n) +{ + return String::format("%ld", n); +} + +String String::number(unsigned long n) +{ + return String::format("%lu", n); +} + +String String::number(long long n) +{ +#if OS(WINDOWS) && !PLATFORM(QT) + return String::format("%I64i", n); +#else + return String::format("%lli", n); +#endif +} + +String String::number(unsigned long long n) +{ +#if OS(WINDOWS) && !PLATFORM(QT) + return String::format("%I64u", n); +#else + return String::format("%llu", n); +#endif +} + +String String::number(double number, unsigned flags, unsigned precision) +{ + NumberToStringBuffer buffer; + + // Mimic String::format("%.[precision]g", ...), but use dtoas rounding facilities. + if (flags & ShouldRoundSignificantFigures) + return String(numberToFixedPrecisionString(number, precision, buffer, flags & ShouldTruncateTrailingZeros)); + + // Mimic String::format("%.[precision]f", ...), but use dtoas rounding facilities. + return String(numberToFixedWidthString(number, precision, buffer)); +} + +int String::toIntStrict(bool* ok, int base) const +{ + if (!m_impl) { + if (ok) + *ok = false; + return 0; + } + return m_impl->toIntStrict(ok, base); +} + +unsigned String::toUIntStrict(bool* ok, int base) const +{ + if (!m_impl) { + if (ok) + *ok = false; + return 0; + } + return m_impl->toUIntStrict(ok, base); +} + +int64_t String::toInt64Strict(bool* ok, int base) const +{ + if (!m_impl) { + if (ok) + *ok = false; + return 0; + } + return m_impl->toInt64Strict(ok, base); +} + +uint64_t String::toUInt64Strict(bool* ok, int base) const +{ + if (!m_impl) { + if (ok) + *ok = false; + return 0; + } + return m_impl->toUInt64Strict(ok, base); +} + +intptr_t String::toIntPtrStrict(bool* ok, int base) const +{ + if (!m_impl) { + if (ok) + *ok = false; + return 0; + } + return m_impl->toIntPtrStrict(ok, base); +} + + +int String::toInt(bool* ok) const +{ + if (!m_impl) { + if (ok) + *ok = false; + return 0; + } + return m_impl->toInt(ok); +} + +unsigned String::toUInt(bool* ok) const +{ + if (!m_impl) { + if (ok) + *ok = false; + return 0; + } + return m_impl->toUInt(ok); +} + +int64_t String::toInt64(bool* ok) const +{ + if (!m_impl) { + if (ok) + *ok = false; + return 0; + } + return m_impl->toInt64(ok); +} + +uint64_t String::toUInt64(bool* ok) const +{ + if (!m_impl) { + if (ok) + *ok = false; + return 0; + } + return m_impl->toUInt64(ok); +} + +intptr_t String::toIntPtr(bool* ok) const +{ + if (!m_impl) { + if (ok) + *ok = false; + return 0; + } + return m_impl->toIntPtr(ok); +} + +double String::toDouble(bool* ok, bool* didReadNumber) const +{ + if (!m_impl) { + if (ok) + *ok = false; + if (didReadNumber) + *didReadNumber = false; + return 0.0; + } + return m_impl->toDouble(ok, didReadNumber); +} + +float String::toFloat(bool* ok, bool* didReadNumber) const +{ + if (!m_impl) { + if (ok) + *ok = false; + if (didReadNumber) + *didReadNumber = false; + return 0.0f; + } + return m_impl->toFloat(ok, didReadNumber); +} + +String String::isolatedCopy() const +{ + if (!m_impl) + return String(); + return m_impl->isolatedCopy(); +} + +void String::split(const String& separator, bool allowEmptyEntries, Vector<String>& result) const +{ + result.clear(); + + unsigned startPos = 0; + size_t endPos; + while ((endPos = find(separator, startPos)) != notFound) { + if (allowEmptyEntries || startPos != endPos) + result.append(substring(startPos, endPos - startPos)); + startPos = endPos + separator.length(); + } + if (allowEmptyEntries || startPos != length()) + result.append(substring(startPos)); +} + +void String::split(const String& separator, Vector<String>& result) const +{ + split(separator, false, result); +} + +void String::split(UChar separator, bool allowEmptyEntries, Vector<String>& result) const +{ + result.clear(); + + unsigned startPos = 0; + size_t endPos; + while ((endPos = find(separator, startPos)) != notFound) { + if (allowEmptyEntries || startPos != endPos) + result.append(substring(startPos, endPos - startPos)); + startPos = endPos + 1; + } + if (allowEmptyEntries || startPos != length()) + result.append(substring(startPos)); +} + +void String::split(UChar separator, Vector<String>& result) const +{ + split(String(&separator, 1), false, result); +} + +CString String::ascii() const +{ + // Printable ASCII characters 32..127 and the null character are + // preserved, characters outside of this range are converted to '?'. + + unsigned length = this->length(); + + if (!length) { + char* characterBuffer; + return CString::newUninitialized(length, characterBuffer); + } + + if (this->is8Bit()) { + const LChar* characters = this->characters8(); + + char* characterBuffer; + CString result = CString::newUninitialized(length, characterBuffer); + + for (unsigned i = 0; i < length; ++i) { + LChar ch = characters[i]; + characterBuffer[i] = ch && (ch < 0x20 || ch > 0x7f) ? '?' : ch; + } + + return result; + } + + const UChar* characters = this->characters16(); + + char* characterBuffer; + CString result = CString::newUninitialized(length, characterBuffer); + + for (unsigned i = 0; i < length; ++i) { + UChar ch = characters[i]; + characterBuffer[i] = ch && (ch < 0x20 || ch > 0x7f) ? '?' : ch; + } + + return result; +} + +CString String::latin1() const +{ + // Basic Latin1 (ISO) encoding - Unicode characters 0..255 are + // preserved, characters outside of this range are converted to '?'. + + unsigned length = this->length(); + const UChar* characters = this->characters(); + + char* characterBuffer; + CString result = CString::newUninitialized(length, characterBuffer); + + for (unsigned i = 0; i < length; ++i) { + UChar ch = characters[i]; + characterBuffer[i] = ch > 0xff ? '?' : ch; + } + + return result; +} + +// Helper to write a three-byte UTF-8 code point to the buffer, caller must check room is available. +static inline void putUTF8Triple(char*& buffer, UChar ch) +{ + ASSERT(ch >= 0x0800); + *buffer++ = static_cast<char>(((ch >> 12) & 0x0F) | 0xE0); + *buffer++ = static_cast<char>(((ch >> 6) & 0x3F) | 0x80); + *buffer++ = static_cast<char>((ch & 0x3F) | 0x80); +} + +CString String::utf8(bool strict) const +{ + unsigned length = this->length(); + + if (!length) + return CString("", 0); + + // Allocate a buffer big enough to hold all the characters + // (an individual UTF-16 UChar can only expand to 3 UTF-8 bytes). + // Optimization ideas, if we find this function is hot: + // * We could speculatively create a CStringBuffer to contain 'length' + // characters, and resize if necessary (i.e. if the buffer contains + // non-ascii characters). (Alternatively, scan the buffer first for + // ascii characters, so we know this will be sufficient). + // * We could allocate a CStringBuffer with an appropriate size to + // have a good chance of being able to write the string into the + // buffer without reallocing (say, 1.5 x length). + if (length > numeric_limits<unsigned>::max() / 3) + return CString(); + Vector<char, 1024> bufferVector(length * 3); + + char* buffer = bufferVector.data(); + + if (is8Bit()) { + const LChar* characters = this->characters8(); + + ConversionResult result = convertLatin1ToUTF8(&characters, characters + length, &buffer, buffer + bufferVector.size()); + ASSERT_UNUSED(result, result != targetExhausted); // (length * 3) should be sufficient for any conversion + } else { + const UChar* characters = this->characters16(); + + ConversionResult result = convertUTF16ToUTF8(&characters, characters + length, &buffer, buffer + bufferVector.size(), strict); + ASSERT(result != targetExhausted); // (length * 3) should be sufficient for any conversion + + // Only produced from strict conversion. + if (result == sourceIllegal) + return CString(); + + // Check for an unconverted high surrogate. + if (result == sourceExhausted) { + if (strict) + return CString(); + // This should be one unpaired high surrogate. Treat it the same + // was as an unpaired high surrogate would have been handled in + // the middle of a string with non-strict conversion - which is + // to say, simply encode it to UTF-8. + ASSERT((characters + 1) == (this->characters() + length)); + ASSERT((*characters >= 0xD800) && (*characters <= 0xDBFF)); + // There should be room left, since one UChar hasn't been converted. + ASSERT((buffer + 3) <= (buffer + bufferVector.size())); + putUTF8Triple(buffer, *characters); + } + } + + return CString(bufferVector.data(), buffer - bufferVector.data()); +} + +String String::fromUTF8(const LChar* stringStart, size_t length) +{ + if (length > numeric_limits<unsigned>::max()) + CRASH(); + + if (!stringStart) + return String(); + + // We'll use a StringImpl as a buffer; if the source string only contains ascii this should be + // the right length, if there are any multi-byte sequences this buffer will be too large. + UChar* buffer; + String stringBuffer(StringImpl::createUninitialized(length, buffer)); + UChar* bufferEnd = buffer + length; + + // Try converting into the buffer. + const char* stringCurrent = reinterpret_cast<const char*>(stringStart); + if (convertUTF8ToUTF16(&stringCurrent, reinterpret_cast<const char *>(stringStart + length), &buffer, bufferEnd) != conversionOK) + return String(); + + // stringBuffer is full (the input must have been all ascii) so just return it! + if (buffer == bufferEnd) + return stringBuffer; + + // stringBuffer served its purpose as a buffer, copy the contents out into a new string. + unsigned utf16Length = buffer - stringBuffer.characters(); + ASSERT(utf16Length < length); + return String(stringBuffer.characters(), utf16Length); +} + +String String::fromUTF8(const LChar* string) +{ + if (!string) + return String(); + return fromUTF8(string, strlen(reinterpret_cast<const char*>(string))); +} + +String String::fromUTF8WithLatin1Fallback(const LChar* string, size_t size) +{ + String utf8 = fromUTF8(string, size); + if (!utf8) + return String(string, size); + return utf8; +} + +// String Operations + +static bool isCharacterAllowedInBase(UChar c, int base) +{ + if (c > 0x7F) + return false; + if (isASCIIDigit(c)) + return c - '0' < base; + if (isASCIIAlpha(c)) { + if (base > 36) + base = 36; + return (c >= 'a' && c < 'a' + base - 10) + || (c >= 'A' && c < 'A' + base - 10); + } + return false; +} + +template <typename IntegralType, typename CharType> +static inline IntegralType toIntegralType(const CharType* data, size_t length, bool* ok, int base) +{ + static const IntegralType integralMax = numeric_limits<IntegralType>::max(); + static const bool isSigned = numeric_limits<IntegralType>::is_signed; + const IntegralType maxMultiplier = integralMax / base; + + IntegralType value = 0; + bool isOk = false; + bool isNegative = false; + + if (!data) + goto bye; + + // skip leading whitespace + while (length && isSpaceOrNewline(*data)) { + length--; + data++; + } + + if (isSigned && length && *data == '-') { + length--; + data++; + isNegative = true; + } else if (length && *data == '+') { + length--; + data++; + } + + if (!length || !isCharacterAllowedInBase(*data, base)) + goto bye; + + while (length && isCharacterAllowedInBase(*data, base)) { + length--; + IntegralType digitValue; + CharType c = *data; + if (isASCIIDigit(c)) + digitValue = c - '0'; + else if (c >= 'a') + digitValue = c - 'a' + 10; + else + digitValue = c - 'A' + 10; + + if (value > maxMultiplier || (value == maxMultiplier && digitValue > (integralMax % base) + isNegative)) + goto bye; + + value = base * value + digitValue; + data++; + } + +#if COMPILER(MSVC) +#pragma warning(push, 0) +#pragma warning(disable:4146) +#endif + + if (isNegative) + value = -value; + +#if COMPILER(MSVC) +#pragma warning(pop) +#endif + + // skip trailing space + while (length && isSpaceOrNewline(*data)) { + length--; + data++; + } + + if (!length) + isOk = true; +bye: + if (ok) + *ok = isOk; + return isOk ? value : 0; +} + +template <typename CharType> +static unsigned lengthOfCharactersAsInteger(const CharType* data, size_t length) +{ + size_t i = 0; + + // Allow leading spaces. + for (; i != length; ++i) { + if (!isSpaceOrNewline(data[i])) + break; + } + + // Allow sign. + if (i != length && (data[i] == '+' || data[i] == '-')) + ++i; + + // Allow digits. + for (; i != length; ++i) { + if (!isASCIIDigit(data[i])) + break; + } + + return i; +} + +int charactersToIntStrict(const LChar* data, size_t length, bool* ok, int base) +{ + return toIntegralType<int, LChar>(data, length, ok, base); +} + +int charactersToIntStrict(const UChar* data, size_t length, bool* ok, int base) +{ + return toIntegralType<int, UChar>(data, length, ok, base); +} + +unsigned charactersToUIntStrict(const LChar* data, size_t length, bool* ok, int base) +{ + return toIntegralType<unsigned, LChar>(data, length, ok, base); +} + +unsigned charactersToUIntStrict(const UChar* data, size_t length, bool* ok, int base) +{ + return toIntegralType<unsigned, UChar>(data, length, ok, base); +} + +int64_t charactersToInt64Strict(const LChar* data, size_t length, bool* ok, int base) +{ + return toIntegralType<int64_t, LChar>(data, length, ok, base); +} + +int64_t charactersToInt64Strict(const UChar* data, size_t length, bool* ok, int base) +{ + return toIntegralType<int64_t, UChar>(data, length, ok, base); +} + +uint64_t charactersToUInt64Strict(const LChar* data, size_t length, bool* ok, int base) +{ + return toIntegralType<uint64_t, LChar>(data, length, ok, base); +} + +uint64_t charactersToUInt64Strict(const UChar* data, size_t length, bool* ok, int base) +{ + return toIntegralType<uint64_t, UChar>(data, length, ok, base); +} + +intptr_t charactersToIntPtrStrict(const LChar* data, size_t length, bool* ok, int base) +{ + return toIntegralType<intptr_t, LChar>(data, length, ok, base); +} + +intptr_t charactersToIntPtrStrict(const UChar* data, size_t length, bool* ok, int base) +{ + return toIntegralType<intptr_t, UChar>(data, length, ok, base); +} + +int charactersToInt(const LChar* data, size_t length, bool* ok) +{ + return toIntegralType<int, LChar>(data, lengthOfCharactersAsInteger<LChar>(data, length), ok, 10); +} + +int charactersToInt(const UChar* data, size_t length, bool* ok) +{ + return toIntegralType<int, UChar>(data, lengthOfCharactersAsInteger(data, length), ok, 10); +} + +unsigned charactersToUInt(const LChar* data, size_t length, bool* ok) +{ + return toIntegralType<unsigned, LChar>(data, lengthOfCharactersAsInteger<LChar>(data, length), ok, 10); +} + +unsigned charactersToUInt(const UChar* data, size_t length, bool* ok) +{ + return toIntegralType<unsigned, UChar>(data, lengthOfCharactersAsInteger<UChar>(data, length), ok, 10); +} + +int64_t charactersToInt64(const LChar* data, size_t length, bool* ok) +{ + return toIntegralType<int64_t, LChar>(data, lengthOfCharactersAsInteger<LChar>(data, length), ok, 10); +} + +int64_t charactersToInt64(const UChar* data, size_t length, bool* ok) +{ + return toIntegralType<int64_t, UChar>(data, lengthOfCharactersAsInteger<UChar>(data, length), ok, 10); +} + +uint64_t charactersToUInt64(const LChar* data, size_t length, bool* ok) +{ + return toIntegralType<uint64_t, LChar>(data, lengthOfCharactersAsInteger<LChar>(data, length), ok, 10); +} + +uint64_t charactersToUInt64(const UChar* data, size_t length, bool* ok) +{ + return toIntegralType<uint64_t, UChar>(data, lengthOfCharactersAsInteger<UChar>(data, length), ok, 10); +} + +intptr_t charactersToIntPtr(const LChar* data, size_t length, bool* ok) +{ + return toIntegralType<intptr_t, LChar>(data, lengthOfCharactersAsInteger<LChar>(data, length), ok, 10); +} + +intptr_t charactersToIntPtr(const UChar* data, size_t length, bool* ok) +{ + return toIntegralType<intptr_t, UChar>(data, lengthOfCharactersAsInteger<UChar>(data, length), ok, 10); +} + +template <typename CharType> +static inline double toDoubleType(const CharType* data, size_t length, bool* ok, bool* didReadNumber) +{ + if (!length) { + if (ok) + *ok = false; + if (didReadNumber) + *didReadNumber = false; + return 0.0; + } + + Vector<char, 256> bytes(length + 1); + for (unsigned i = 0; i < length; ++i) + bytes[i] = data[i] < 0x7F ? data[i] : '?'; + bytes[length] = '\0'; + char* start = bytes.data(); + char* end; + double val = WTF::strtod(start, &end); + if (ok) + *ok = (end == 0 || *end == '\0'); + if (didReadNumber) + *didReadNumber = end - start; + return val; +} + +double charactersToDouble(const LChar* data, size_t length, bool* ok, bool* didReadNumber) +{ + return toDoubleType<LChar>(data, length, ok, didReadNumber); +} + +double charactersToDouble(const UChar* data, size_t length, bool* ok, bool* didReadNumber) +{ + return toDoubleType<UChar>(data, length, ok, didReadNumber); +} + +float charactersToFloat(const LChar* data, size_t length, bool* ok, bool* didReadNumber) +{ + // FIXME: This will return ok even when the string fits into a double but not a float. + return static_cast<float>(toDoubleType<LChar>(data, length, ok, didReadNumber)); +} + +float charactersToFloat(const UChar* data, size_t length, bool* ok, bool* didReadNumber) +{ + // FIXME: This will return ok even when the string fits into a double but not a float. + return static_cast<float>(toDoubleType<UChar>(data, length, ok, didReadNumber)); +} + +const String& emptyString() +{ + DEFINE_STATIC_LOCAL(String, emptyString, (StringImpl::empty())); + return emptyString; +} + +} // namespace WTF + +#ifndef NDEBUG +// For use in the debugger +String* string(const char*); +Vector<char> asciiDebug(StringImpl* impl); +Vector<char> asciiDebug(String& string); + +void String::show() const +{ + fprintf(stderr, "%s\n", asciiDebug(impl()).data()); +} + +String* string(const char* s) +{ + // leaks memory! + return new String(s); +} + +Vector<char> asciiDebug(StringImpl* impl) +{ + if (!impl) + return asciiDebug(String("[null]").impl()); + + Vector<char> buffer; + unsigned length = impl->length(); + const UChar* characters = impl->characters(); + + buffer.resize(length + 1); + for (unsigned i = 0; i < length; ++i) { + UChar ch = characters[i]; + buffer[i] = ch && (ch < 0x20 || ch > 0x7f) ? '?' : ch; + } + buffer[length] = '\0'; + + return buffer; +} + +Vector<char> asciiDebug(String& string) +{ + return asciiDebug(string.impl()); +} + +#endif diff --git a/Source/JavaScriptCore/wtf/text/WTFString.h b/Source/JavaScriptCore/wtf/text/WTFString.h new file mode 100644 index 000000000..3cecc0afd --- /dev/null +++ b/Source/JavaScriptCore/wtf/text/WTFString.h @@ -0,0 +1,648 @@ +/* + * (C) 1999 Lars Knoll (knoll@kde.org) + * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010 Apple Inc. All rights reserved. + * + * This library 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 library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#ifndef WTFString_h +#define WTFString_h + +// This file would be called String.h, but that conflicts with <string.h> +// on systems without case-sensitive file systems. + +#include "ASCIIFastPath.h" +#include "StringImpl.h" + +#ifdef __OBJC__ +#include <objc/objc.h> +#endif + +#if USE(CF) +typedef const struct __CFString * CFStringRef; +#endif + +#if PLATFORM(QT) +QT_BEGIN_NAMESPACE +class QString; +QT_END_NAMESPACE +#include <QDataStream> +#endif + +#if PLATFORM(WX) +class wxString; +#endif + +#if PLATFORM(BLACKBERRY) +namespace BlackBerry { +namespace WebKit { + class WebString; +} +} +#endif + +namespace WTF { + +class CString; +struct StringHash; + +// Declarations of string operations + +WTF_EXPORT_PRIVATE int charactersToIntStrict(const LChar*, size_t, bool* ok = 0, int base = 10); +WTF_EXPORT_PRIVATE int charactersToIntStrict(const UChar*, size_t, bool* ok = 0, int base = 10); +WTF_EXPORT_PRIVATE unsigned charactersToUIntStrict(const LChar*, size_t, bool* ok = 0, int base = 10); +WTF_EXPORT_PRIVATE unsigned charactersToUIntStrict(const UChar*, size_t, bool* ok = 0, int base = 10); +int64_t charactersToInt64Strict(const LChar*, size_t, bool* ok = 0, int base = 10); +int64_t charactersToInt64Strict(const UChar*, size_t, bool* ok = 0, int base = 10); +uint64_t charactersToUInt64Strict(const LChar*, size_t, bool* ok = 0, int base = 10); +uint64_t charactersToUInt64Strict(const UChar*, size_t, bool* ok = 0, int base = 10); +intptr_t charactersToIntPtrStrict(const LChar*, size_t, bool* ok = 0, int base = 10); +intptr_t charactersToIntPtrStrict(const UChar*, size_t, bool* ok = 0, int base = 10); + +int charactersToInt(const LChar*, size_t, bool* ok = 0); // ignores trailing garbage +int charactersToInt(const UChar*, size_t, bool* ok = 0); // ignores trailing garbage +unsigned charactersToUInt(const LChar*, size_t, bool* ok = 0); // ignores trailing garbage +unsigned charactersToUInt(const UChar*, size_t, bool* ok = 0); // ignores trailing garbage +int64_t charactersToInt64(const LChar*, size_t, bool* ok = 0); // ignores trailing garbage +int64_t charactersToInt64(const UChar*, size_t, bool* ok = 0); // ignores trailing garbage +uint64_t charactersToUInt64(const LChar*, size_t, bool* ok = 0); // ignores trailing garbage +uint64_t charactersToUInt64(const UChar*, size_t, bool* ok = 0); // ignores trailing garbage +intptr_t charactersToIntPtr(const LChar*, size_t, bool* ok = 0); // ignores trailing garbage +intptr_t charactersToIntPtr(const UChar*, size_t, bool* ok = 0); // ignores trailing garbage + +WTF_EXPORT_PRIVATE double charactersToDouble(const LChar*, size_t, bool* ok = 0, bool* didReadNumber = 0); +WTF_EXPORT_PRIVATE double charactersToDouble(const UChar*, size_t, bool* ok = 0, bool* didReadNumber = 0); +float charactersToFloat(const LChar*, size_t, bool* ok = 0, bool* didReadNumber = 0); +float charactersToFloat(const UChar*, size_t, bool* ok = 0, bool* didReadNumber = 0); + +enum FloatConversionFlags { + ShouldRoundSignificantFigures = 1 << 0, + ShouldRoundDecimalPlaces = 1 << 1, + ShouldTruncateTrailingZeros = 1 << 2 +}; + +template<bool isSpecialCharacter(UChar)> bool isAllSpecialCharacters(const UChar*, size_t); + +class String { +public: + // Construct a null string, distinguishable from an empty string. + String() { } + + // Construct a string with UTF-16 data. + WTF_EXPORT_PRIVATE String(const UChar* characters, unsigned length); + + // Construct a string by copying the contents of a vector. To avoid + // copying, consider using String::adopt instead. + template<size_t inlineCapacity> + explicit String(const Vector<UChar, inlineCapacity>&); + + // Construct a string with UTF-16 data, from a null-terminated source. + WTF_EXPORT_PRIVATE String(const UChar*); + + // Construct a string with latin1 data. + WTF_EXPORT_PRIVATE String(const LChar* characters, unsigned length); + WTF_EXPORT_PRIVATE String(const char* characters, unsigned length); + + // Construct a string with latin1 data, from a null-terminated source. + WTF_EXPORT_PRIVATE String(const LChar* characters); + WTF_EXPORT_PRIVATE String(const char* characters); + + // Construct a string referencing an existing StringImpl. + String(StringImpl* impl) : m_impl(impl) { } + String(PassRefPtr<StringImpl> impl) : m_impl(impl) { } + String(RefPtr<StringImpl> impl) : m_impl(impl) { } + + // Inline the destructor. + ALWAYS_INLINE ~String() { } + + void swap(String& o) { m_impl.swap(o.m_impl); } + + static String adopt(StringBuffer<LChar>& buffer) { return StringImpl::adopt(buffer); } + static String adopt(StringBuffer<UChar>& buffer) { return StringImpl::adopt(buffer); } + template<size_t inlineCapacity> + static String adopt(Vector<UChar, inlineCapacity>& vector) { return StringImpl::adopt(vector); } + + bool isNull() const { return !m_impl; } + bool isEmpty() const { return !m_impl || !m_impl->length(); } + + StringImpl* impl() const { return m_impl.get(); } + + unsigned length() const + { + if (!m_impl) + return 0; + return m_impl->length(); + } + + const UChar* characters() const + { + if (!m_impl) + return 0; + return m_impl->characters(); + } + + const LChar* characters8() const + { + if (!m_impl) + return 0; + ASSERT(m_impl->is8Bit()); + return m_impl->characters8(); + } + + const UChar* characters16() const + { + if (!m_impl) + return 0; + ASSERT(!m_impl->is8Bit()); + return m_impl->characters16(); + } + + template <typename CharType> + inline const CharType* getCharacters() const; + + bool is8Bit() const { return m_impl->is8Bit(); } + + WTF_EXPORT_PRIVATE CString ascii() const; + WTF_EXPORT_PRIVATE CString latin1() const; + WTF_EXPORT_PRIVATE CString utf8(bool strict = false) const; + + UChar operator[](unsigned index) const + { + if (!m_impl || index >= m_impl->length()) + return 0; + return m_impl->characters()[index]; + } + + WTF_EXPORT_PRIVATE static String number(short); + WTF_EXPORT_PRIVATE static String number(unsigned short); + WTF_EXPORT_PRIVATE static String number(int); + WTF_EXPORT_PRIVATE static String number(unsigned); + WTF_EXPORT_PRIVATE static String number(long); + WTF_EXPORT_PRIVATE static String number(unsigned long); + WTF_EXPORT_PRIVATE static String number(long long); + WTF_EXPORT_PRIVATE static String number(unsigned long long); + WTF_EXPORT_PRIVATE static String number(double, unsigned = ShouldRoundSignificantFigures | ShouldTruncateTrailingZeros, unsigned precision = 6); + + // Find a single character or string, also with match function & latin1 forms. + size_t find(UChar c, unsigned start = 0) const + { return m_impl ? m_impl->find(c, start) : notFound; } + size_t find(const String& str, unsigned start = 0) const + { return m_impl ? m_impl->find(str.impl(), start) : notFound; } + size_t find(CharacterMatchFunctionPtr matchFunction, unsigned start = 0) const + { return m_impl ? m_impl->find(matchFunction, start) : notFound; } + size_t find(const LChar* str, unsigned start = 0) const + { return m_impl ? m_impl->find(str, start) : notFound; } + + // Find the last instance of a single character or string. + size_t reverseFind(UChar c, unsigned start = UINT_MAX) const + { return m_impl ? m_impl->reverseFind(c, start) : notFound; } + size_t reverseFind(const String& str, unsigned start = UINT_MAX) const + { return m_impl ? m_impl->reverseFind(str.impl(), start) : notFound; } + + // Case insensitive string matching. + size_t findIgnoringCase(const LChar* str, unsigned start = 0) const + { return m_impl ? m_impl->findIgnoringCase(str, start) : notFound; } + size_t findIgnoringCase(const String& str, unsigned start = 0) const + { return m_impl ? m_impl->findIgnoringCase(str.impl(), start) : notFound; } + size_t reverseFindIgnoringCase(const String& str, unsigned start = UINT_MAX) const + { return m_impl ? m_impl->reverseFindIgnoringCase(str.impl(), start) : notFound; } + + // Wrappers for find & reverseFind adding dynamic sensitivity check. + size_t find(const LChar* str, unsigned start, bool caseSensitive) const + { return caseSensitive ? find(str, start) : findIgnoringCase(str, start); } + size_t find(const String& str, unsigned start, bool caseSensitive) const + { return caseSensitive ? find(str, start) : findIgnoringCase(str, start); } + size_t reverseFind(const String& str, unsigned start, bool caseSensitive) const + { return caseSensitive ? reverseFind(str, start) : reverseFindIgnoringCase(str, start); } + + WTF_EXPORT_PRIVATE const UChar* charactersWithNullTermination(); + + WTF_EXPORT_PRIVATE UChar32 characterStartingAt(unsigned) const; // Ditto. + + bool contains(UChar c) const { return find(c) != notFound; } + bool contains(const LChar* str, bool caseSensitive = true) const { return find(str, 0, caseSensitive) != notFound; } + bool contains(const String& str, bool caseSensitive = true) const { return find(str, 0, caseSensitive) != notFound; } + + bool startsWith(const String& s, bool caseSensitive = true) const + { return m_impl ? m_impl->startsWith(s.impl(), caseSensitive) : s.isEmpty(); } + bool endsWith(const String& s, bool caseSensitive = true) const + { return m_impl ? m_impl->endsWith(s.impl(), caseSensitive) : s.isEmpty(); } + + WTF_EXPORT_PRIVATE void append(const String&); + WTF_EXPORT_PRIVATE void append(LChar); + void append(char c) { append(static_cast<LChar>(c)); }; + WTF_EXPORT_PRIVATE void append(UChar); + WTF_EXPORT_PRIVATE void append(const UChar*, unsigned length); + WTF_EXPORT_PRIVATE void insert(const String&, unsigned pos); + void insert(const UChar*, unsigned length, unsigned pos); + + String& replace(UChar a, UChar b) { if (m_impl) m_impl = m_impl->replace(a, b); return *this; } + String& replace(UChar a, const String& b) { if (m_impl) m_impl = m_impl->replace(a, b.impl()); return *this; } + String& replace(const String& a, const String& b) { if (m_impl) m_impl = m_impl->replace(a.impl(), b.impl()); return *this; } + String& replace(unsigned index, unsigned len, const String& b) { if (m_impl) m_impl = m_impl->replace(index, len, b.impl()); return *this; } + + void makeLower() { if (m_impl) m_impl = m_impl->lower(); } + void makeUpper() { if (m_impl) m_impl = m_impl->upper(); } + void fill(UChar c) { if (m_impl) m_impl = m_impl->fill(c); } + + WTF_EXPORT_PRIVATE void truncate(unsigned len); + WTF_EXPORT_PRIVATE void remove(unsigned pos, int len = 1); + + WTF_EXPORT_PRIVATE String substring(unsigned pos, unsigned len = UINT_MAX) const; + String substringSharingImpl(unsigned pos, unsigned len = UINT_MAX) const; + String left(unsigned len) const { return substring(0, len); } + String right(unsigned len) const { return substring(length() - len, len); } + + // Returns a lowercase/uppercase version of the string + WTF_EXPORT_PRIVATE String lower() const; + WTF_EXPORT_PRIVATE String upper() const; + + WTF_EXPORT_PRIVATE String stripWhiteSpace() const; + WTF_EXPORT_PRIVATE String stripWhiteSpace(IsWhiteSpaceFunctionPtr) const; + WTF_EXPORT_PRIVATE String simplifyWhiteSpace() const; + WTF_EXPORT_PRIVATE String simplifyWhiteSpace(IsWhiteSpaceFunctionPtr) const; + + WTF_EXPORT_PRIVATE String removeCharacters(CharacterMatchFunctionPtr) const; + template<bool isSpecialCharacter(UChar)> bool isAllSpecialCharacters() const; + + // Return the string with case folded for case insensitive comparison. + WTF_EXPORT_PRIVATE String foldCase() const; + +#if !PLATFORM(QT) + WTF_EXPORT_PRIVATE static String format(const char *, ...) WTF_ATTRIBUTE_PRINTF(1, 2); +#else + WTF_EXPORT_PRIVATE static String format(const char *, ...); +#endif + + // Returns an uninitialized string. The characters needs to be written + // into the buffer returned in data before the returned string is used. + // Failure to do this will have unpredictable results. + static String createUninitialized(unsigned length, UChar*& data) { return StringImpl::createUninitialized(length, data); } + + WTF_EXPORT_PRIVATE void split(const String& separator, Vector<String>& result) const; + WTF_EXPORT_PRIVATE void split(const String& separator, bool allowEmptyEntries, Vector<String>& result) const; + WTF_EXPORT_PRIVATE void split(UChar separator, Vector<String>& result) const; + WTF_EXPORT_PRIVATE void split(UChar separator, bool allowEmptyEntries, Vector<String>& result) const; + + WTF_EXPORT_PRIVATE int toIntStrict(bool* ok = 0, int base = 10) const; + WTF_EXPORT_PRIVATE unsigned toUIntStrict(bool* ok = 0, int base = 10) const; + WTF_EXPORT_PRIVATE int64_t toInt64Strict(bool* ok = 0, int base = 10) const; + WTF_EXPORT_PRIVATE uint64_t toUInt64Strict(bool* ok = 0, int base = 10) const; + WTF_EXPORT_PRIVATE intptr_t toIntPtrStrict(bool* ok = 0, int base = 10) const; + + WTF_EXPORT_PRIVATE int toInt(bool* ok = 0) const; + WTF_EXPORT_PRIVATE unsigned toUInt(bool* ok = 0) const; + int64_t toInt64(bool* ok = 0) const; + WTF_EXPORT_PRIVATE uint64_t toUInt64(bool* ok = 0) const; + WTF_EXPORT_PRIVATE intptr_t toIntPtr(bool* ok = 0) const; + WTF_EXPORT_PRIVATE double toDouble(bool* ok = 0, bool* didReadNumber = 0) const; + WTF_EXPORT_PRIVATE float toFloat(bool* ok = 0, bool* didReadNumber = 0) const; + + bool percentage(int& percentage) const; + + WTF_EXPORT_PRIVATE String isolatedCopy() const; + + // Prevent Strings from being implicitly convertable to bool as it will be ambiguous on any platform that + // allows implicit conversion to another pointer type (e.g., Mac allows implicit conversion to NSString*). + typedef struct ImplicitConversionFromWTFStringToBoolDisallowedA* (String::*UnspecifiedBoolTypeA); + typedef struct ImplicitConversionFromWTFStringToBoolDisallowedB* (String::*UnspecifiedBoolTypeB); + operator UnspecifiedBoolTypeA() const; + operator UnspecifiedBoolTypeB() const; + +#if USE(CF) + String(CFStringRef); + CFStringRef createCFString() const; +#endif + +#ifdef __OBJC__ + String(NSString*); + + // This conversion maps NULL to "", which loses the meaning of NULL, but we + // need this mapping because AppKit crashes when passed nil NSStrings. + operator NSString*() const { if (!m_impl) return @""; return *m_impl; } +#endif + +#if PLATFORM(QT) + String(const QString&); + String(const QStringRef&); + operator QString() const; +#endif + +#if PLATFORM(WX) + WTF_EXPORT_PRIVATE String(const wxString&); + WTF_EXPORT_PRIVATE operator wxString() const; +#endif + +#if PLATFORM(BLACKBERRY) + String(const BlackBerry::WebKit::WebString&); + operator BlackBerry::WebKit::WebString() const; +#endif + + // String::fromUTF8 will return a null string if + // the input data contains invalid UTF-8 sequences. + WTF_EXPORT_PRIVATE static String fromUTF8(const LChar*, size_t); + WTF_EXPORT_PRIVATE static String fromUTF8(const LChar*); + static String fromUTF8(const char* s, size_t length) { return fromUTF8(reinterpret_cast<const LChar*>(s), length); }; + static String fromUTF8(const char* s) { return fromUTF8(reinterpret_cast<const LChar*>(s)); }; + + // Tries to convert the passed in string to UTF-8, but will fall back to Latin-1 if the string is not valid UTF-8. + WTF_EXPORT_PRIVATE static String fromUTF8WithLatin1Fallback(const LChar*, size_t); + static String fromUTF8WithLatin1Fallback(const char* s, size_t length) { return fromUTF8WithLatin1Fallback(reinterpret_cast<const LChar*>(s), length); }; + + // Determines the writing direction using the Unicode Bidi Algorithm rules P2 and P3. + WTF::Unicode::Direction defaultWritingDirection(bool* hasStrongDirectionality = 0) const + { + if (m_impl) + return m_impl->defaultWritingDirection(hasStrongDirectionality); + if (hasStrongDirectionality) + *hasStrongDirectionality = false; + return WTF::Unicode::LeftToRight; + } + + bool containsOnlyASCII() const; + bool containsOnlyLatin1() const; + bool containsOnlyWhitespace() const { return !m_impl || m_impl->containsOnlyWhitespace(); } + + // Hash table deleted values, which are only constructed and never copied or destroyed. + String(WTF::HashTableDeletedValueType) : m_impl(WTF::HashTableDeletedValue) { } + bool isHashTableDeletedValue() const { return m_impl.isHashTableDeletedValue(); } + +#ifndef NDEBUG + void show() const; +#endif + +private: + RefPtr<StringImpl> m_impl; +}; + +#if PLATFORM(QT) +QDataStream& operator<<(QDataStream& stream, const String& str); +QDataStream& operator>>(QDataStream& stream, String& str); +#endif + +inline String& operator+=(String& a, const String& b) { a.append(b); return a; } + +inline bool operator==(const String& a, const String& b) { return equal(a.impl(), b.impl()); } +inline bool operator==(const String& a, const LChar* b) { return equal(a.impl(), b); } +inline bool operator==(const String& a, const char* b) { return equal(a.impl(), reinterpret_cast<const LChar*>(b)); } +inline bool operator==(const LChar* a, const String& b) { return equal(a, b.impl()); } +inline bool operator==(const char* a, const String& b) { return equal(reinterpret_cast<const LChar*>(a), b.impl()); } +template<size_t inlineCapacity> +inline bool operator==(const Vector<char, inlineCapacity>& a, const String& b) { return equal(b.impl(), a.data(), a.size()); } +template<size_t inlineCapacity> +inline bool operator==(const String& a, const Vector<char, inlineCapacity>& b) { return b == a; } + + +inline bool operator!=(const String& a, const String& b) { return !equal(a.impl(), b.impl()); } +inline bool operator!=(const String& a, const LChar* b) { return !equal(a.impl(), b); } +inline bool operator!=(const String& a, const char* b) { return !equal(a.impl(), reinterpret_cast<const LChar*>(b)); } +inline bool operator!=(const LChar* a, const String& b) { return !equal(a, b.impl()); } +inline bool operator!=(const char* a, const String& b) { return !equal(reinterpret_cast<const LChar*>(a), b.impl()); } +template<size_t inlineCapacity> +inline bool operator!=(const Vector<char, inlineCapacity>& a, const String& b) { return !(a == b); } +template<size_t inlineCapacity> +inline bool operator!=(const String& a, const Vector<char, inlineCapacity>& b) { return b != a; } + +inline bool equalIgnoringCase(const String& a, const String& b) { return equalIgnoringCase(a.impl(), b.impl()); } +inline bool equalIgnoringCase(const String& a, const LChar* b) { return equalIgnoringCase(a.impl(), b); } +inline bool equalIgnoringCase(const String& a, const char* b) { return equalIgnoringCase(a.impl(), reinterpret_cast<const LChar*>(b)); } +inline bool equalIgnoringCase(const LChar* a, const String& b) { return equalIgnoringCase(a, b.impl()); } +inline bool equalIgnoringCase(const char* a, const String& b) { return equalIgnoringCase(reinterpret_cast<const LChar*>(a), b.impl()); } + +inline bool equalPossiblyIgnoringCase(const String& a, const String& b, bool ignoreCase) +{ + return ignoreCase ? equalIgnoringCase(a, b) : (a == b); +} + +inline bool equalIgnoringNullity(const String& a, const String& b) { return equalIgnoringNullity(a.impl(), b.impl()); } + +template<size_t inlineCapacity> +inline bool equalIgnoringNullity(const Vector<UChar, inlineCapacity>& a, const String& b) { return equalIgnoringNullity(a, b.impl()); } + +inline bool operator!(const String& str) { return str.isNull(); } + +inline void swap(String& a, String& b) { a.swap(b); } + +// Definitions of string operations + +template<size_t inlineCapacity> +String::String(const Vector<UChar, inlineCapacity>& vector) + : m_impl(vector.size() ? StringImpl::create(vector.data(), vector.size()) : 0) +{ +} + +template<> +inline const LChar* String::getCharacters<LChar>() const +{ + ASSERT(is8Bit()); + return characters8(); +} + +template<> +inline const UChar* String::getCharacters<UChar>() const +{ + ASSERT(!is8Bit()); + return characters16(); +} + +inline bool String::containsOnlyLatin1() const +{ + if (isEmpty()) + return true; + + if (is8Bit()) + return true; + + const UChar* characters = characters16(); + UChar ored = 0; + for (size_t i = 0; i < m_impl->length(); ++i) + ored |= characters[i]; + return !(ored & 0xFF00); +} + + +#ifdef __OBJC__ +// This is for situations in WebKit where the long standing behavior has been +// "nil if empty", so we try to maintain longstanding behavior for the sake of +// entrenched clients +inline NSString* nsStringNilIfEmpty(const String& str) { return str.isEmpty() ? nil : (NSString*)str; } +#endif + +inline bool String::containsOnlyASCII() const +{ + if (isEmpty()) + return true; + + if (is8Bit()) + return charactersAreAllASCII(characters8(), m_impl->length()); + + return charactersAreAllASCII(characters16(), m_impl->length()); +} + +WTF_EXPORT_PRIVATE int codePointCompare(const String&, const String&); + +inline bool codePointCompareLessThan(const String& a, const String& b) +{ + return codePointCompare(a.impl(), b.impl()) < 0; +} + +inline size_t find(const LChar* characters, unsigned length, LChar matchCharacter, unsigned index = 0) +{ + while (index < length) { + if (characters[index] == matchCharacter) + return index; + ++index; + } + return notFound; +} + +inline size_t find(const UChar* characters, unsigned length, UChar matchCharacter, unsigned index = 0) +{ + while (index < length) { + if (characters[index] == matchCharacter) + return index; + ++index; + } + return notFound; +} + +inline size_t find(const LChar* characters, unsigned length, CharacterMatchFunctionPtr matchFunction, unsigned index = 0) +{ + while (index < length) { + if (matchFunction(characters[index])) + return index; + ++index; + } + return notFound; +} + +inline size_t find(const UChar* characters, unsigned length, CharacterMatchFunctionPtr matchFunction, unsigned index = 0) +{ + while (index < length) { + if (matchFunction(characters[index])) + return index; + ++index; + } + return notFound; +} + +inline size_t reverseFind(const LChar* characters, unsigned length, LChar matchCharacter, unsigned index = UINT_MAX) +{ + if (!length) + return notFound; + if (index >= length) + index = length - 1; + while (characters[index] != matchCharacter) { + if (!index--) + return notFound; + } + return index; +} + +inline size_t reverseFind(const UChar* characters, unsigned length, UChar matchCharacter, unsigned index = UINT_MAX) +{ + if (!length) + return notFound; + if (index >= length) + index = length - 1; + while (characters[index] != matchCharacter) { + if (!index--) + return notFound; + } + return index; +} + +inline void append(Vector<UChar>& vector, const String& string) +{ + vector.append(string.characters(), string.length()); +} + +inline void appendNumber(Vector<UChar>& vector, unsigned char number) +{ + int numberLength = number > 99 ? 3 : (number > 9 ? 2 : 1); + size_t vectorSize = vector.size(); + vector.grow(vectorSize + numberLength); + + switch (numberLength) { + case 3: + vector[vectorSize + 2] = number % 10 + '0'; + number /= 10; + + case 2: + vector[vectorSize + 1] = number % 10 + '0'; + number /= 10; + + case 1: + vector[vectorSize] = number % 10 + '0'; + } +} + +template<bool isSpecialCharacter(UChar)> inline bool isAllSpecialCharacters(const UChar* characters, size_t length) +{ + for (size_t i = 0; i < length; ++i) { + if (!isSpecialCharacter(characters[i])) + return false; + } + return true; +} + +template<bool isSpecialCharacter(UChar)> inline bool String::isAllSpecialCharacters() const +{ + return WTF::isAllSpecialCharacters<isSpecialCharacter>(characters(), length()); +} + +// StringHash is the default hash for String +template<typename T> struct DefaultHash; +template<> struct DefaultHash<String> { + typedef StringHash Hash; +}; + +template <> struct VectorTraits<String> : SimpleClassVectorTraits { }; + +// Shared global empty string. +WTF_EXPORT_PRIVATE const String& emptyString(); + +} + +using WTF::CString; +using WTF::String; +using WTF::emptyString; +using WTF::append; +using WTF::appendNumber; +using WTF::charactersAreAllASCII; +using WTF::charactersToIntStrict; +using WTF::charactersToUIntStrict; +using WTF::charactersToInt64Strict; +using WTF::charactersToUInt64Strict; +using WTF::charactersToIntPtrStrict; +using WTF::charactersToInt; +using WTF::charactersToUInt; +using WTF::charactersToInt64; +using WTF::charactersToUInt64; +using WTF::charactersToIntPtr; +using WTF::charactersToDouble; +using WTF::charactersToFloat; +using WTF::equal; +using WTF::equalIgnoringCase; +using WTF::find; +using WTF::isAllSpecialCharacters; +using WTF::isSpaceOrNewline; +using WTF::reverseFind; +using WTF::ShouldRoundDecimalPlaces; + +#include "AtomicString.h" +#endif diff --git a/Source/JavaScriptCore/wtf/threads/BinarySemaphore.cpp b/Source/JavaScriptCore/wtf/threads/BinarySemaphore.cpp new file mode 100644 index 000000000..5f60aaf22 --- /dev/null +++ b/Source/JavaScriptCore/wtf/threads/BinarySemaphore.cpp @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2010 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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 APPLE INC. AND ITS 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 APPLE INC. OR ITS 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 "config.h" +#include "BinarySemaphore.h" + +#if !PLATFORM(WIN) + +namespace WTF { + +BinarySemaphore::BinarySemaphore() + : m_isSet(false) +{ +} + +BinarySemaphore::~BinarySemaphore() +{ +} + +void BinarySemaphore::signal() +{ + MutexLocker locker(m_mutex); + + m_isSet = true; + m_condition.signal(); +} + +bool BinarySemaphore::wait(double absoluteTime) +{ + MutexLocker locker(m_mutex); + + bool timedOut = false; + while (!m_isSet) { + timedOut = !m_condition.timedWait(m_mutex, absoluteTime); + if (timedOut) + return false; + } + + // Reset the semaphore. + m_isSet = false; + return true; +} + +} // namespace WTF + +#endif // !PLATFORM(WIN) diff --git a/Source/JavaScriptCore/wtf/threads/BinarySemaphore.h b/Source/JavaScriptCore/wtf/threads/BinarySemaphore.h new file mode 100644 index 000000000..8e82207e9 --- /dev/null +++ b/Source/JavaScriptCore/wtf/threads/BinarySemaphore.h @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2010, 2011 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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 APPLE INC. AND ITS 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 APPLE INC. OR ITS 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. + */ + +#ifndef BinarySemaphore_h +#define BinarySemaphore_h + +#include <wtf/Noncopyable.h> +#include <wtf/ThreadingPrimitives.h> + +namespace WTF { + +class BinarySemaphore { + WTF_MAKE_NONCOPYABLE(BinarySemaphore); + +public: + BinarySemaphore(); + ~BinarySemaphore(); + + void signal(); + bool wait(double absoluteTime); + +#if PLATFORM(WIN) + HANDLE event() const { return m_event; } +#endif + +private: +#if PLATFORM(WIN) + HANDLE m_event; +#else + bool m_isSet; + + Mutex m_mutex; + ThreadCondition m_condition; +#endif +}; + +} // namespace WTF + +using WTF::BinarySemaphore; + +#endif // BinarySemaphore_h diff --git a/Source/JavaScriptCore/wtf/threads/win/BinarySemaphoreWin.cpp b/Source/JavaScriptCore/wtf/threads/win/BinarySemaphoreWin.cpp new file mode 100644 index 000000000..adc9e9b90 --- /dev/null +++ b/Source/JavaScriptCore/wtf/threads/win/BinarySemaphoreWin.cpp @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2011 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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 APPLE INC. AND ITS 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 APPLE INC. OR ITS 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 "config.h" +#include "BinarySemaphore.h" + +namespace WTF { + +BinarySemaphore::BinarySemaphore() + : m_event(::CreateEventW(0, FALSE, FALSE, 0)) +{ +} + +BinarySemaphore::~BinarySemaphore() +{ + ::CloseHandle(m_event); +} + +void BinarySemaphore::signal() +{ + ::SetEvent(m_event); +} + +bool BinarySemaphore::wait(double absoluteTime) +{ + DWORD interval = absoluteTimeToWaitTimeoutInterval(absoluteTime); + if (!interval) { + // Consider the wait to have timed out, even if the event has already been signaled, to + // match the WTF::ThreadCondition implementation. + return false; + } + + DWORD result = ::WaitForSingleObjectEx(m_event, interval, FALSE); + switch (result) { + case WAIT_OBJECT_0: + // The event was signaled. + return true; + + case WAIT_TIMEOUT: + // The wait timed out. + return false; + + case WAIT_FAILED: + ASSERT_WITH_MESSAGE(false, "::WaitForSingleObjectEx failed with error %lu", ::GetLastError()); + return false; + default: + ASSERT_WITH_MESSAGE(false, "::WaitForSingleObjectEx returned unexpected result %lu", result); + return false; + } +} + +} // namespace WTF diff --git a/Source/JavaScriptCore/wtf/unicode/CharacterNames.h b/Source/JavaScriptCore/wtf/unicode/CharacterNames.h new file mode 100644 index 000000000..10fdbf0ef --- /dev/null +++ b/Source/JavaScriptCore/wtf/unicode/CharacterNames.h @@ -0,0 +1,144 @@ +/* + * Copyright (C) 2007, 2009, 2010 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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 APPLE COMPUTER, INC. ``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 APPLE COMPUTER, INC. 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. + */ + +#ifndef CharacterNames_h +#define CharacterNames_h + +#include "Unicode.h" + +namespace WTF { +namespace Unicode { + +// Names here are taken from the Unicode standard. + +// Most of these are UChar constants, not UChar32, which makes them +// more convenient for WebCore code that mostly uses UTF-16. + +const UChar32 aegeanWordSeparatorLine = 0x10100; +const UChar32 aegeanWordSeparatorDot = 0x10101; +const UChar blackCircle = 0x25CF; +const UChar blackSquare = 0x25A0; +const UChar blackUpPointingTriangle = 0x25B2; +const UChar bullet = 0x2022; +const UChar bullseye = 0x25CE; +const UChar carriageReturn = 0x000D; +const UChar ethiopicPrefaceColon = 0x1366; +const UChar ethiopicWordspace = 0x1361; +const UChar fisheye = 0x25C9; +const UChar hebrewPunctuationGeresh = 0x05F3; +const UChar hebrewPunctuationGershayim = 0x05F4; +const UChar horizontalEllipsis = 0x2026; +const UChar hyphen = 0x2010; +const UChar hyphenMinus = 0x002D; +const UChar ideographicComma = 0x3001; +const UChar ideographicFullStop = 0x3002; +const UChar ideographicSpace = 0x3000; +const UChar leftDoubleQuotationMark = 0x201C; +const UChar leftSingleQuotationMark = 0x2018; +const UChar leftToRightEmbed = 0x202A; +const UChar leftToRightMark = 0x200E; +const UChar leftToRightOverride = 0x202D; +const UChar minusSign = 0x2212; +const UChar newlineCharacter = 0x000A; +const UChar noBreakSpace = 0x00A0; +const UChar objectReplacementCharacter = 0xFFFC; +const UChar popDirectionalFormatting = 0x202C; +const UChar replacementCharacter = 0xFFFD; +const UChar rightDoubleQuotationMark = 0x201D; +const UChar rightSingleQuotationMark = 0x2019; +const UChar rightToLeftEmbed = 0x202B; +const UChar rightToLeftMark = 0x200F; +const UChar rightToLeftOverride = 0x202E; +const UChar sesameDot = 0xFE45; +const UChar softHyphen = 0x00AD; +const UChar space = 0x0020; +const UChar tibetanMarkIntersyllabicTsheg = 0x0F0B; +const UChar tibetanMarkDelimiterTshegBstar = 0x0F0C; +const UChar32 ugariticWordDivider = 0x1039F; +const UChar whiteBullet = 0x25E6; +const UChar whiteCircle = 0x25CB; +const UChar whiteSesameDot = 0xFE46; +const UChar whiteUpPointingTriangle = 0x25B3; +const UChar yenSign = 0x00A5; +const UChar zeroWidthJoiner = 0x200D; +const UChar zeroWidthNonJoiner = 0x200C; +const UChar zeroWidthSpace = 0x200B; +const UChar zeroWidthNoBreakSpace = 0xFEFF; + +} // namespace Unicode +} // namespace WTF + +using WTF::Unicode::aegeanWordSeparatorLine; +using WTF::Unicode::aegeanWordSeparatorDot; +using WTF::Unicode::blackCircle; +using WTF::Unicode::blackSquare; +using WTF::Unicode::blackUpPointingTriangle; +using WTF::Unicode::bullet; +using WTF::Unicode::bullseye; +using WTF::Unicode::carriageReturn; +using WTF::Unicode::ethiopicPrefaceColon; +using WTF::Unicode::ethiopicWordspace; +using WTF::Unicode::fisheye; +using WTF::Unicode::hebrewPunctuationGeresh; +using WTF::Unicode::hebrewPunctuationGershayim; +using WTF::Unicode::horizontalEllipsis; +using WTF::Unicode::hyphen; +using WTF::Unicode::hyphenMinus; +using WTF::Unicode::ideographicComma; +using WTF::Unicode::ideographicFullStop; +using WTF::Unicode::ideographicSpace; +using WTF::Unicode::leftDoubleQuotationMark; +using WTF::Unicode::leftSingleQuotationMark; +using WTF::Unicode::leftToRightEmbed; +using WTF::Unicode::leftToRightMark; +using WTF::Unicode::leftToRightOverride; +using WTF::Unicode::minusSign; +using WTF::Unicode::newlineCharacter; +using WTF::Unicode::noBreakSpace; +using WTF::Unicode::objectReplacementCharacter; +using WTF::Unicode::popDirectionalFormatting; +using WTF::Unicode::replacementCharacter; +using WTF::Unicode::rightDoubleQuotationMark; +using WTF::Unicode::rightSingleQuotationMark; +using WTF::Unicode::rightToLeftEmbed; +using WTF::Unicode::rightToLeftMark; +using WTF::Unicode::rightToLeftOverride; +using WTF::Unicode::sesameDot; +using WTF::Unicode::softHyphen; +using WTF::Unicode::space; +using WTF::Unicode::tibetanMarkIntersyllabicTsheg; +using WTF::Unicode::tibetanMarkDelimiterTshegBstar; +using WTF::Unicode::ugariticWordDivider; +using WTF::Unicode::whiteBullet; +using WTF::Unicode::whiteCircle; +using WTF::Unicode::whiteSesameDot; +using WTF::Unicode::whiteUpPointingTriangle; +using WTF::Unicode::yenSign; +using WTF::Unicode::zeroWidthJoiner; +using WTF::Unicode::zeroWidthNonJoiner; +using WTF::Unicode::zeroWidthSpace; +using WTF::Unicode::zeroWidthNoBreakSpace; + +#endif // CharacterNames_h diff --git a/Source/JavaScriptCore/wtf/unicode/Collator.h b/Source/JavaScriptCore/wtf/unicode/Collator.h new file mode 100644 index 000000000..00ab16e6a --- /dev/null +++ b/Source/JavaScriptCore/wtf/unicode/Collator.h @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2008 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of Apple Computer, Inc. ("Apple") 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 APPLE AND ITS 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 APPLE OR ITS 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. + */ + +#ifndef WTF_Collator_h +#define WTF_Collator_h + +#include <wtf/FastAllocBase.h> +#include <wtf/Noncopyable.h> +#include <wtf/PassOwnPtr.h> +#include <wtf/unicode/Unicode.h> + +#if USE(ICU_UNICODE) && !UCONFIG_NO_COLLATION +struct UCollator; +#endif + +namespace WTF { + + class Collator { + WTF_MAKE_NONCOPYABLE(Collator); WTF_MAKE_FAST_ALLOCATED; + public: + enum Result { Equal = 0, Greater = 1, Less = -1 }; + + Collator(const char* locale); // Parsing is lenient; e.g. language identifiers (such as "en-US") are accepted, too. + ~Collator(); + void setOrderLowerFirst(bool); + + static PassOwnPtr<Collator> userDefault(); + + Result collate(const ::UChar*, size_t, const ::UChar*, size_t) const; + + private: +#if USE(ICU_UNICODE) && !UCONFIG_NO_COLLATION + void createCollator() const; + void releaseCollator(); + mutable UCollator* m_collator; +#endif + char* m_locale; + bool m_lowerFirst; + }; +} + +using WTF::Collator; + +#endif diff --git a/Source/JavaScriptCore/wtf/unicode/CollatorDefault.cpp b/Source/JavaScriptCore/wtf/unicode/CollatorDefault.cpp new file mode 100644 index 000000000..7bde114a4 --- /dev/null +++ b/Source/JavaScriptCore/wtf/unicode/CollatorDefault.cpp @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2008 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of Apple Computer, Inc. ("Apple") 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 APPLE AND ITS 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 APPLE OR ITS 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 "config.h" +#include "Collator.h" + +#if !USE(ICU_UNICODE) || UCONFIG_NO_COLLATION + +namespace WTF { + +Collator::Collator(const char*) +{ +} + +Collator::~Collator() +{ +} + +void Collator::setOrderLowerFirst(bool) +{ +} + +PassOwnPtr<Collator> Collator::userDefault() +{ + return adoptPtr(new Collator(0)); +} + +// A default implementation for platforms that lack Unicode-aware collation. +Collator::Result Collator::collate(const UChar* lhs, size_t lhsLength, const UChar* rhs, size_t rhsLength) const +{ + int lmin = lhsLength < rhsLength ? lhsLength : rhsLength; + int l = 0; + while (l < lmin && *lhs == *rhs) { + lhs++; + rhs++; + l++; + } + + if (l < lmin) + return (*lhs > *rhs) ? Greater : Less; + + if (lhsLength == rhsLength) + return Equal; + + return (lhsLength > rhsLength) ? Greater : Less; +} + +} + +#endif diff --git a/Source/JavaScriptCore/wtf/unicode/ScriptCodesFromICU.h b/Source/JavaScriptCore/wtf/unicode/ScriptCodesFromICU.h new file mode 100644 index 000000000..4760399a1 --- /dev/null +++ b/Source/JavaScriptCore/wtf/unicode/ScriptCodesFromICU.h @@ -0,0 +1,153 @@ +/* + * Copyright (C) 1997-2006, International Business Machines + * Corporation and others. All Rights Reserved. + */ + +#ifndef WTF_ScriptCodesFromICU_h +#define WTF_ScriptCodesFromICU_h + +/** + * Constants for ISO 15924 script codes. + * + * Many of these script codes - those from Unicode's ScriptNames.txt - + * are character property values for Unicode's Script property. + * See UAX #24 Script Names (http://www.unicode.org/reports/tr24/). + * + * Starting with ICU 3.6, constants for most ISO 15924 script codes + * are included (currently excluding private-use codes Qaaa..Qabx). + * For scripts for which there are codes in ISO 15924 but which are not + * used in the Unicode Character Database (UCD), there are no Unicode characters + * associated with those scripts. + * + * For example, there are no characters that have a UCD script code of + * Hans or Hant. All Han ideographs have the Hani script code. + * The Hans and Hant script codes are used with CLDR data. + * + * ISO 15924 script codes are included for use with CLDR and similar. + * + * @stable ICU 2.2 + */ +typedef enum UScriptCode { + USCRIPT_INVALID_CODE = -1, + USCRIPT_COMMON = 0 , /* Zyyy */ + USCRIPT_INHERITED = 1, /* Qaai */ + USCRIPT_ARABIC = 2, /* Arab */ + USCRIPT_ARMENIAN = 3, /* Armn */ + USCRIPT_BENGALI = 4, /* Beng */ + USCRIPT_BOPOMOFO = 5, /* Bopo */ + USCRIPT_CHEROKEE = 6, /* Cher */ + USCRIPT_COPTIC = 7, /* Copt */ + USCRIPT_CYRILLIC = 8, /* Cyrl */ + USCRIPT_DESERET = 9, /* Dsrt */ + USCRIPT_DEVANAGARI = 10, /* Deva */ + USCRIPT_ETHIOPIC = 11, /* Ethi */ + USCRIPT_GEORGIAN = 12, /* Geor */ + USCRIPT_GOTHIC = 13, /* Goth */ + USCRIPT_GREEK = 14, /* Grek */ + USCRIPT_GUJARATI = 15, /* Gujr */ + USCRIPT_GURMUKHI = 16, /* Guru */ + USCRIPT_HAN = 17, /* Hani */ + USCRIPT_HANGUL = 18, /* Hang */ + USCRIPT_HEBREW = 19, /* Hebr */ + USCRIPT_HIRAGANA = 20, /* Hira */ + USCRIPT_KANNADA = 21, /* Knda */ + USCRIPT_KATAKANA = 22, /* Kana */ + USCRIPT_KHMER = 23, /* Khmr */ + USCRIPT_LAO = 24, /* Laoo */ + USCRIPT_LATIN = 25, /* Latn */ + USCRIPT_MALAYALAM = 26, /* Mlym */ + USCRIPT_MONGOLIAN = 27, /* Mong */ + USCRIPT_MYANMAR = 28, /* Mymr */ + USCRIPT_OGHAM = 29, /* Ogam */ + USCRIPT_OLD_ITALIC = 30, /* Ital */ + USCRIPT_ORIYA = 31, /* Orya */ + USCRIPT_RUNIC = 32, /* Runr */ + USCRIPT_SINHALA = 33, /* Sinh */ + USCRIPT_SYRIAC = 34, /* Syrc */ + USCRIPT_TAMIL = 35, /* Taml */ + USCRIPT_TELUGU = 36, /* Telu */ + USCRIPT_THAANA = 37, /* Thaa */ + USCRIPT_THAI = 38, /* Thai */ + USCRIPT_TIBETAN = 39, /* Tibt */ + /** Canadian_Aboriginal script. @stable ICU 2.6 */ + USCRIPT_CANADIAN_ABORIGINAL = 40, /* Cans */ + /** Canadian_Aboriginal script (alias). @stable ICU 2.2 */ + USCRIPT_UCAS = USCRIPT_CANADIAN_ABORIGINAL, + USCRIPT_YI = 41, /* Yiii */ + USCRIPT_TAGALOG = 42, /* Tglg */ + USCRIPT_HANUNOO = 43, /* Hano */ + USCRIPT_BUHID = 44, /* Buhd */ + USCRIPT_TAGBANWA = 45, /* Tagb */ + + /* New scripts in Unicode 4 @stable ICU 2.6 */ + USCRIPT_BRAILLE = 46, /* Brai */ + USCRIPT_CYPRIOT = 47, /* Cprt */ + USCRIPT_LIMBU = 48, /* Limb */ + USCRIPT_LINEAR_B = 49, /* Linb */ + USCRIPT_OSMANYA = 50, /* Osma */ + USCRIPT_SHAVIAN = 51, /* Shaw */ + USCRIPT_TAI_LE = 52, /* Tale */ + USCRIPT_UGARITIC = 53, /* Ugar */ + + /** New script code in Unicode 4.0.1 @stable ICU 3.0 */ + USCRIPT_KATAKANA_OR_HIRAGANA = 54,/*Hrkt */ + +#ifndef U_HIDE_DRAFT_API + /* New scripts in Unicode 4.1 @draft ICU 3.4 */ + USCRIPT_BUGINESE = 55, /* Bugi */ + USCRIPT_GLAGOLITIC = 56, /* Glag */ + USCRIPT_KHAROSHTHI = 57, /* Khar */ + USCRIPT_SYLOTI_NAGRI = 58, /* Sylo */ + USCRIPT_NEW_TAI_LUE = 59, /* Talu */ + USCRIPT_TIFINAGH = 60, /* Tfng */ + USCRIPT_OLD_PERSIAN = 61, /* Xpeo */ + + /* New script codes from ISO 15924 @draft ICU 3.6 */ + USCRIPT_BALINESE = 62, /* Bali */ + USCRIPT_BATAK = 63, /* Batk */ + USCRIPT_BLISSYMBOLS = 64, /* Blis */ + USCRIPT_BRAHMI = 65, /* Brah */ + USCRIPT_CHAM = 66, /* Cham */ + USCRIPT_CIRTH = 67, /* Cirt */ + USCRIPT_OLD_CHURCH_SLAVONIC_CYRILLIC = 68, /* Cyrs */ + USCRIPT_DEMOTIC_EGYPTIAN = 69, /* Egyd */ + USCRIPT_HIERATIC_EGYPTIAN = 70, /* Egyh */ + USCRIPT_EGYPTIAN_HIEROGLYPHS = 71, /* Egyp */ + USCRIPT_KHUTSURI = 72, /* Geok */ + USCRIPT_SIMPLIFIED_HAN = 73, /* Hans */ + USCRIPT_TRADITIONAL_HAN = 74, /* Hant */ + USCRIPT_PAHAWH_HMONG = 75, /* Hmng */ + USCRIPT_OLD_HUNGARIAN = 76, /* Hung */ + USCRIPT_HARAPPAN_INDUS = 77, /* Inds */ + USCRIPT_JAVANESE = 78, /* Java */ + USCRIPT_KAYAH_LI = 79, /* Kali */ + USCRIPT_LATIN_FRAKTUR = 80, /* Latf */ + USCRIPT_LATIN_GAELIC = 81, /* Latg */ + USCRIPT_LEPCHA = 82, /* Lepc */ + USCRIPT_LINEAR_A = 83, /* Lina */ + USCRIPT_MANDAEAN = 84, /* Mand */ + USCRIPT_MAYAN_HIEROGLYPHS = 85, /* Maya */ + USCRIPT_MEROITIC = 86, /* Mero */ + USCRIPT_NKO = 87, /* Nkoo */ + USCRIPT_ORKHON = 88, /* Orkh */ + USCRIPT_OLD_PERMIC = 89, /* Perm */ + USCRIPT_PHAGS_PA = 90, /* Phag */ + USCRIPT_PHOENICIAN = 91, /* Phnx */ + USCRIPT_PHONETIC_POLLARD = 92, /* Plrd */ + USCRIPT_RONGORONGO = 93, /* Roro */ + USCRIPT_SARATI = 94, /* Sara */ + USCRIPT_ESTRANGELO_SYRIAC = 95, /* Syre */ + USCRIPT_WESTERN_SYRIAC = 96, /* Syrj */ + USCRIPT_EASTERN_SYRIAC = 97, /* Syrn */ + USCRIPT_TENGWAR = 98, /* Teng */ + USCRIPT_VAI = 99, /* Vaii */ + USCRIPT_VISIBLE_SPEECH = 100, /* Visp */ + USCRIPT_CUNEIFORM = 101,/* Xsux */ + USCRIPT_UNWRITTEN_LANGUAGES = 102,/* Zxxx */ + USCRIPT_UNKNOWN = 103,/* Zzzz */ /* Unknown="Code for uncoded script", for unassigned code points */ + /* Private use codes from Qaaa - Qabx are not supported*/ +#endif /* U_HIDE_DRAFT_API */ + USCRIPT_CODE_LIMIT = 104 +} UScriptCode; + +#endif diff --git a/Source/JavaScriptCore/wtf/unicode/UTF8.cpp b/Source/JavaScriptCore/wtf/unicode/UTF8.cpp new file mode 100644 index 000000000..8ea5c6992 --- /dev/null +++ b/Source/JavaScriptCore/wtf/unicode/UTF8.cpp @@ -0,0 +1,449 @@ +/* + * Copyright (C) 2007 Apple Inc. All rights reserved. + * Copyright (C) 2010 Patrick Gansterer <paroga@paroga.com> + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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 APPLE COMPUTER, INC. ``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 APPLE COMPUTER, INC. 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 "config.h" +#include "UTF8.h" + +#include "ASCIICType.h" +#include <wtf/StringHasher.h> +#include <wtf/unicode/CharacterNames.h> + +namespace WTF { +namespace Unicode { + +inline int inlineUTF8SequenceLengthNonASCII(char b0) +{ + if ((b0 & 0xC0) != 0xC0) + return 0; + if ((b0 & 0xE0) == 0xC0) + return 2; + if ((b0 & 0xF0) == 0xE0) + return 3; + if ((b0 & 0xF8) == 0xF0) + return 4; + return 0; +} + +inline int inlineUTF8SequenceLength(char b0) +{ + return isASCII(b0) ? 1 : inlineUTF8SequenceLengthNonASCII(b0); +} + +int UTF8SequenceLength(char b0) +{ + return isASCII(b0) ? 1 : inlineUTF8SequenceLengthNonASCII(b0); +} + +int decodeUTF8Sequence(const char* sequence) +{ + // Handle 0-byte sequences (never valid). + const unsigned char b0 = sequence[0]; + const int length = inlineUTF8SequenceLength(b0); + if (length == 0) + return -1; + + // Handle 1-byte sequences (plain ASCII). + const unsigned char b1 = sequence[1]; + if (length == 1) { + if (b1) + return -1; + return b0; + } + + // Handle 2-byte sequences. + if ((b1 & 0xC0) != 0x80) + return -1; + const unsigned char b2 = sequence[2]; + if (length == 2) { + if (b2) + return -1; + const int c = ((b0 & 0x1F) << 6) | (b1 & 0x3F); + if (c < 0x80) + return -1; + return c; + } + + // Handle 3-byte sequences. + if ((b2 & 0xC0) != 0x80) + return -1; + const unsigned char b3 = sequence[3]; + if (length == 3) { + if (b3) + return -1; + const int c = ((b0 & 0xF) << 12) | ((b1 & 0x3F) << 6) | (b2 & 0x3F); + if (c < 0x800) + return -1; + // UTF-16 surrogates should never appear in UTF-8 data. + if (c >= 0xD800 && c <= 0xDFFF) + return -1; + return c; + } + + // Handle 4-byte sequences. + if ((b3 & 0xC0) != 0x80) + return -1; + const unsigned char b4 = sequence[4]; + if (length == 4) { + if (b4) + return -1; + const int c = ((b0 & 0x7) << 18) | ((b1 & 0x3F) << 12) | ((b2 & 0x3F) << 6) | (b3 & 0x3F); + if (c < 0x10000 || c > 0x10FFFF) + return -1; + return c; + } + + return -1; +} + +// Once the bits are split out into bytes of UTF-8, this is a mask OR-ed +// into the first byte, depending on how many bytes follow. There are +// as many entries in this table as there are UTF-8 sequence types. +// (I.e., one byte sequence, two byte... etc.). Remember that sequencs +// for *legal* UTF-8 will be 4 or fewer bytes total. +static const unsigned char firstByteMark[7] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC }; + +ConversionResult convertLatin1ToUTF8( + const LChar** sourceStart, const LChar* sourceEnd, + char** targetStart, char* targetEnd) +{ + ConversionResult result = conversionOK; + const LChar* source = *sourceStart; + char* target = *targetStart; + while (source < sourceEnd) { + UChar32 ch; + unsigned short bytesToWrite = 0; + const UChar32 byteMask = 0xBF; + const UChar32 byteMark = 0x80; + const LChar* oldSource = source; // In case we have to back up because of target overflow. + ch = static_cast<unsigned short>(*source++); + + // Figure out how many bytes the result will require + if (ch < (UChar32)0x80) + bytesToWrite = 1; + else + bytesToWrite = 2; + + target += bytesToWrite; + if (target > targetEnd) { + source = oldSource; // Back up source pointer! + target -= bytesToWrite; + result = targetExhausted; + break; + } + switch (bytesToWrite) { // note: everything falls through. + case 2: + *--target = (char)((ch | byteMark) & byteMask); + ch >>= 6; + case 1: + *--target = (char)(ch | firstByteMark[bytesToWrite]); + } + target += bytesToWrite; + } + *sourceStart = source; + *targetStart = target; + return result; +} + +ConversionResult convertUTF16ToUTF8( + const UChar** sourceStart, const UChar* sourceEnd, + char** targetStart, char* targetEnd, bool strict) +{ + ConversionResult result = conversionOK; + const UChar* source = *sourceStart; + char* target = *targetStart; + while (source < sourceEnd) { + UChar32 ch; + unsigned short bytesToWrite = 0; + const UChar32 byteMask = 0xBF; + const UChar32 byteMark = 0x80; + const UChar* oldSource = source; // In case we have to back up because of target overflow. + ch = static_cast<unsigned short>(*source++); + // If we have a surrogate pair, convert to UChar32 first. + if (ch >= 0xD800 && ch <= 0xDBFF) { + // If the 16 bits following the high surrogate are in the source buffer... + if (source < sourceEnd) { + UChar32 ch2 = static_cast<unsigned short>(*source); + // If it's a low surrogate, convert to UChar32. + if (ch2 >= 0xDC00 && ch2 <= 0xDFFF) { + ch = ((ch - 0xD800) << 10) + (ch2 - 0xDC00) + 0x0010000; + ++source; + } else if (strict) { // it's an unpaired high surrogate + --source; // return to the illegal value itself + result = sourceIllegal; + break; + } + } else { // We don't have the 16 bits following the high surrogate. + --source; // return to the high surrogate + result = sourceExhausted; + break; + } + } else if (strict) { + // UTF-16 surrogate values are illegal in UTF-32 + if (ch >= 0xDC00 && ch <= 0xDFFF) { + --source; // return to the illegal value itself + result = sourceIllegal; + break; + } + } + // Figure out how many bytes the result will require + if (ch < (UChar32)0x80) { + bytesToWrite = 1; + } else if (ch < (UChar32)0x800) { + bytesToWrite = 2; + } else if (ch < (UChar32)0x10000) { + bytesToWrite = 3; + } else if (ch < (UChar32)0x110000) { + bytesToWrite = 4; + } else { + bytesToWrite = 3; + ch = replacementCharacter; + } + + target += bytesToWrite; + if (target > targetEnd) { + source = oldSource; // Back up source pointer! + target -= bytesToWrite; + result = targetExhausted; + break; + } + switch (bytesToWrite) { // note: everything falls through. + case 4: *--target = (char)((ch | byteMark) & byteMask); ch >>= 6; + case 3: *--target = (char)((ch | byteMark) & byteMask); ch >>= 6; + case 2: *--target = (char)((ch | byteMark) & byteMask); ch >>= 6; + case 1: *--target = (char)(ch | firstByteMark[bytesToWrite]); + } + target += bytesToWrite; + } + *sourceStart = source; + *targetStart = target; + return result; +} + +// This must be called with the length pre-determined by the first byte. +// If presented with a length > 4, this returns false. The Unicode +// definition of UTF-8 goes up to 4-byte sequences. +static bool isLegalUTF8(const unsigned char* source, int length) +{ + unsigned char a; + const unsigned char* srcptr = source + length; + switch (length) { + default: return false; + // Everything else falls through when "true"... + case 4: if ((a = (*--srcptr)) < 0x80 || a > 0xBF) return false; + case 3: if ((a = (*--srcptr)) < 0x80 || a > 0xBF) return false; + case 2: if ((a = (*--srcptr)) > 0xBF) return false; + + switch (*source) { + // no fall-through in this inner switch + case 0xE0: if (a < 0xA0) return false; break; + case 0xED: if (a > 0x9F) return false; break; + case 0xF0: if (a < 0x90) return false; break; + case 0xF4: if (a > 0x8F) return false; break; + default: if (a < 0x80) return false; + } + + case 1: if (*source >= 0x80 && *source < 0xC2) return false; + } + if (*source > 0xF4) + return false; + return true; +} + +// Magic values subtracted from a buffer value during UTF8 conversion. +// This table contains as many values as there might be trailing bytes +// in a UTF-8 sequence. +static const UChar32 offsetsFromUTF8[6] = { 0x00000000UL, 0x00003080UL, 0x000E2080UL, + 0x03C82080UL, 0xFA082080UL, 0x82082080UL }; + +static inline UChar32 readUTF8Sequence(const char*& sequence, unsigned length) +{ + UChar32 character = 0; + + // The cases all fall through. + switch (length) { + case 6: character += static_cast<unsigned char>(*sequence++); character <<= 6; + case 5: character += static_cast<unsigned char>(*sequence++); character <<= 6; + case 4: character += static_cast<unsigned char>(*sequence++); character <<= 6; + case 3: character += static_cast<unsigned char>(*sequence++); character <<= 6; + case 2: character += static_cast<unsigned char>(*sequence++); character <<= 6; + case 1: character += static_cast<unsigned char>(*sequence++); + } + + return character - offsetsFromUTF8[length - 1]; +} + +ConversionResult convertUTF8ToUTF16( + const char** sourceStart, const char* sourceEnd, + UChar** targetStart, UChar* targetEnd, bool strict) +{ + ConversionResult result = conversionOK; + const char* source = *sourceStart; + UChar* target = *targetStart; + while (source < sourceEnd) { + int utf8SequenceLength = inlineUTF8SequenceLength(*source); + if (sourceEnd - source < utf8SequenceLength) { + result = sourceExhausted; + break; + } + // Do this check whether lenient or strict + if (!isLegalUTF8(reinterpret_cast<const unsigned char*>(source), utf8SequenceLength)) { + result = sourceIllegal; + break; + } + + UChar32 character = readUTF8Sequence(source, utf8SequenceLength); + + if (target >= targetEnd) { + source -= utf8SequenceLength; // Back up source pointer! + result = targetExhausted; + break; + } + + if (U_IS_BMP(character)) { + // UTF-16 surrogate values are illegal in UTF-32 + if (U_IS_SURROGATE(character)) { + if (strict) { + source -= utf8SequenceLength; // return to the illegal value itself + result = sourceIllegal; + break; + } else + *target++ = replacementCharacter; + } else + *target++ = character; // normal case + } else if (U_IS_SUPPLEMENTARY(character)) { + // target is a character in range 0xFFFF - 0x10FFFF + if (target + 1 >= targetEnd) { + source -= utf8SequenceLength; // Back up source pointer! + result = targetExhausted; + break; + } + *target++ = U16_LEAD(character); + *target++ = U16_TRAIL(character); + } else { + if (strict) { + source -= utf8SequenceLength; // return to the start + result = sourceIllegal; + break; // Bail out; shouldn't continue + } else + *target++ = replacementCharacter; + } + } + *sourceStart = source; + *targetStart = target; + return result; +} + +unsigned calculateStringHashAndLengthFromUTF8(const char* data, const char* dataEnd, unsigned& dataLength, unsigned& utf16Length) +{ + if (!data) + return 0; + + StringHasher stringHasher; + dataLength = 0; + utf16Length = 0; + + while (data < dataEnd || (!dataEnd && *data)) { + if (isASCII(*data)) { + stringHasher.addCharacter(*data++); + dataLength++; + utf16Length++; + continue; + } + + int utf8SequenceLength = inlineUTF8SequenceLengthNonASCII(*data); + dataLength += utf8SequenceLength; + + if (!dataEnd) { + for (int i = 1; i < utf8SequenceLength; ++i) { + if (!data[i]) + return 0; + } + } else if (dataEnd - data < utf8SequenceLength) + return 0; + + if (!isLegalUTF8(reinterpret_cast<const unsigned char*>(data), utf8SequenceLength)) + return 0; + + UChar32 character = readUTF8Sequence(data, utf8SequenceLength); + ASSERT(!isASCII(character)); + + if (U_IS_BMP(character)) { + // UTF-16 surrogate values are illegal in UTF-32 + if (U_IS_SURROGATE(character)) + return 0; + stringHasher.addCharacter(static_cast<UChar>(character)); // normal case + utf16Length++; + } else if (U_IS_SUPPLEMENTARY(character)) { + stringHasher.addCharacters(static_cast<UChar>(U16_LEAD(character)), + static_cast<UChar>(U16_TRAIL(character))); + utf16Length += 2; + } else + return 0; + } + + return stringHasher.hash(); +} + +bool equalUTF16WithUTF8(const UChar* a, const UChar* aEnd, const char* b, const char* bEnd) +{ + while (b < bEnd) { + if (isASCII(*b)) { + if (*a++ != *b++) + return false; + continue; + } + + int utf8SequenceLength = inlineUTF8SequenceLengthNonASCII(*b); + + if (bEnd - b < utf8SequenceLength) + return false; + + if (!isLegalUTF8(reinterpret_cast<const unsigned char*>(b), utf8SequenceLength)) + return 0; + + UChar32 character = readUTF8Sequence(b, utf8SequenceLength); + ASSERT(!isASCII(character)); + + if (U_IS_BMP(character)) { + // UTF-16 surrogate values are illegal in UTF-32 + if (U_IS_SURROGATE(character)) + return false; + if (*a++ != character) + return false; + } else if (U_IS_SUPPLEMENTARY(character)) { + if (*a++ != U16_LEAD(character)) + return false; + if (*a++ != U16_TRAIL(character)) + return false; + } else + return false; + } + + return a == aEnd; +} + +} // namespace Unicode +} // namespace WTF diff --git a/Source/JavaScriptCore/wtf/unicode/UTF8.h b/Source/JavaScriptCore/wtf/unicode/UTF8.h new file mode 100644 index 000000000..bd3dd8a0a --- /dev/null +++ b/Source/JavaScriptCore/wtf/unicode/UTF8.h @@ -0,0 +1,84 @@ +/* + * Copyright (C) 2007 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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 APPLE COMPUTER, INC. ``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 APPLE COMPUTER, INC. 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. + */ + +#ifndef WTF_UTF8_h +#define WTF_UTF8_h + +#include "Unicode.h" + +namespace WTF { +namespace Unicode { + + // Given a first byte, gives the length of the UTF-8 sequence it begins. + // Returns 0 for bytes that are not legal starts of UTF-8 sequences. + // Only allows sequences of up to 4 bytes, since that works for all Unicode characters (U-00000000 to U-0010FFFF). + int UTF8SequenceLength(char); + + // Takes a null-terminated C-style string with a UTF-8 sequence in it and converts it to a character. + // Only allows Unicode characters (U-00000000 to U-0010FFFF). + // Returns -1 if the sequence is not valid (including presence of extra bytes). + int decodeUTF8Sequence(const char*); + + typedef enum { + conversionOK, // conversion successful + sourceExhausted, // partial character in source, but hit end + targetExhausted, // insuff. room in target for conversion + sourceIllegal // source sequence is illegal/malformed + } ConversionResult; + + // These conversion functions take a "strict" argument. When this + // flag is set to strict, both irregular sequences and isolated surrogates + // will cause an error. When the flag is set to lenient, both irregular + // sequences and isolated surrogates are converted. + // + // Whether the flag is strict or lenient, all illegal sequences will cause + // an error return. This includes sequences such as: <F4 90 80 80>, <C0 80>, + // or <A0> in UTF-8, and values above 0x10FFFF in UTF-32. Conformant code + // must check for illegal sequences. + // + // When the flag is set to lenient, characters over 0x10FFFF are converted + // to the replacement character; otherwise (when the flag is set to strict) + // they constitute an error. + + ConversionResult convertUTF8ToUTF16( + const char** sourceStart, const char* sourceEnd, + UChar** targetStart, UChar* targetEnd, bool strict = true); + + ConversionResult convertLatin1ToUTF8( + const LChar** sourceStart, const LChar* sourceEnd, + char** targetStart, char* targetEnd); + + ConversionResult convertUTF16ToUTF8( + const UChar** sourceStart, const UChar* sourceEnd, + char** targetStart, char* targetEnd, bool strict = true); + + unsigned calculateStringHashAndLengthFromUTF8(const char* data, const char* dataEnd, unsigned& dataLength, unsigned& utf16Length); + + bool equalUTF16WithUTF8(const UChar* a, const UChar* aEnd, const char* b, const char* bEnd); + +} // namespace Unicode +} // namespace WTF + +#endif // WTF_UTF8_h diff --git a/Source/JavaScriptCore/wtf/unicode/Unicode.h b/Source/JavaScriptCore/wtf/unicode/Unicode.h new file mode 100644 index 000000000..cc44476a6 --- /dev/null +++ b/Source/JavaScriptCore/wtf/unicode/Unicode.h @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2006 George Staikos <staikos@kde.org> + * Copyright (C) 2006, 2008, 2009 Apple Inc. All rights reserved. + * Copyright (C) 2007-2009 Torch Mobile, Inc. + * + * This library 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 library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#ifndef WTF_UNICODE_H +#define WTF_UNICODE_H + +#include <wtf/Assertions.h> + +// Define platform neutral 8 bit character type (L is for Latin-1). +typedef unsigned char LChar; + +#if USE(QT4_UNICODE) +#include "qt4/UnicodeQt4.h" +#elif USE(ICU_UNICODE) +#include <wtf/unicode/icu/UnicodeIcu.h> +#elif USE(GLIB_UNICODE) +#include <wtf/unicode/glib/UnicodeGLib.h> +#elif USE(WINCE_UNICODE) +#include <wtf/unicode/wince/UnicodeWinCE.h> +#else +#error "Unknown Unicode implementation" +#endif + +COMPILE_ASSERT(sizeof(UChar) == 2, UCharIsTwoBytes); + +#endif // WTF_UNICODE_H diff --git a/Source/JavaScriptCore/wtf/unicode/UnicodeMacrosFromICU.h b/Source/JavaScriptCore/wtf/unicode/UnicodeMacrosFromICU.h new file mode 100644 index 000000000..09a7036e3 --- /dev/null +++ b/Source/JavaScriptCore/wtf/unicode/UnicodeMacrosFromICU.h @@ -0,0 +1,100 @@ +/* + * Copyright (C) 1999-2004, International Business Machines Corporation and others. All Rights Reserved. + * + */ + +#ifndef UnicodeMacrosFromICU_h +#define UnicodeMacrosFromICU_h + +// some defines from ICU + +#define U_IS_BMP(c) ((UChar32)(c)<=0xffff) +#define U16_IS_LEAD(c) (((c)&0xfffffc00)==0xd800) +#define U16_IS_TRAIL(c) (((c)&0xfffffc00)==0xdc00) +#define U16_SURROGATE_OFFSET ((0xd800<<10UL)+0xdc00-0x10000) +#define U16_GET_SUPPLEMENTARY(lead, trail) \ + (((UChar32)(lead)<<10UL)+(UChar32)(trail)-U16_SURROGATE_OFFSET) + +#define U16_LEAD(supplementary) (UChar)(((supplementary)>>10)+0xd7c0) +#define U16_TRAIL(supplementary) (UChar)(((supplementary)&0x3ff)|0xdc00) +#define U16_LENGTH(c) ((uint32_t)(c) <= 0xffff ? 1 : 2) + +#define U_IS_SUPPLEMENTARY(c) ((UChar32)((c)-0x10000)<=0xfffff) +#define U_IS_SURROGATE(c) (((c)&0xfffff800)==0xd800) +#define U16_IS_SINGLE(c) !U_IS_SURROGATE(c) +#define U16_IS_SURROGATE(c) U_IS_SURROGATE(c) +#define U16_IS_SURROGATE_LEAD(c) (((c)&0x400)==0) + +#define U16_GET(s, start, i, length, c) { \ + (c)=(s)[i]; \ + if(U16_IS_SURROGATE(c)) { \ + uint16_t __c2; \ + if(U16_IS_SURROGATE_LEAD(c)) { \ + if((i)+1<(length) && U16_IS_TRAIL(__c2=(s)[(i)+1])) { \ + (c)=U16_GET_SUPPLEMENTARY((c), __c2); \ + } \ + } else { \ + if((i)-1>=(start) && U16_IS_LEAD(__c2=(s)[(i)-1])) { \ + (c)=U16_GET_SUPPLEMENTARY(__c2, (c)); \ + } \ + } \ + } \ +} + +#define U16_PREV(s, start, i, c) { \ + (c)=(s)[--(i)]; \ + if(U16_IS_TRAIL(c)) { \ + uint16_t __c2; \ + if((i)>(start) && U16_IS_LEAD(__c2=(s)[(i)-1])) { \ + --(i); \ + (c)=U16_GET_SUPPLEMENTARY(__c2, (c)); \ + } \ + } \ +} + +#define U16_BACK_1(s, start, i) { \ + if(U16_IS_TRAIL((s)[--(i)]) && (i)>(start) && U16_IS_LEAD((s)[(i)-1])) { \ + --(i); \ + } \ +} + +#define U16_NEXT(s, i, length, c) { \ + (c)=(s)[(i)++]; \ + if(U16_IS_LEAD(c)) { \ + uint16_t __c2; \ + if((i)<(length) && U16_IS_TRAIL(__c2=(s)[(i)])) { \ + ++(i); \ + (c)=U16_GET_SUPPLEMENTARY((c), __c2); \ + } \ + } \ +} + +#define U16_FWD_1(s, i, length) { \ + if(U16_IS_LEAD((s)[(i)++]) && (i)<(length) && U16_IS_TRAIL((s)[i])) { \ + ++(i); \ + } \ +} + +#define U_MASK(x) ((uint32_t)1<<(x)) + +#define U8_MAX_LENGTH 4 + +#define U8_APPEND_UNSAFE(s, i, c) { \ + if((uint32_t)(c)<=0x7f) { \ + (s)[(i)++]=(uint8_t)(c); \ + } else { \ + if((uint32_t)(c)<=0x7ff) { \ + (s)[(i)++]=(uint8_t)(((c)>>6)|0xc0); \ + } else { \ + if((uint32_t)(c)<=0xffff) { \ + (s)[(i)++]=(uint8_t)(((c)>>12)|0xe0); \ + } else { \ + (s)[(i)++]=(uint8_t)(((c)>>18)|0xf0); \ + (s)[(i)++]=(uint8_t)((((c)>>12)&0x3f)|0x80); \ + } \ + (s)[(i)++]=(uint8_t)((((c)>>6)&0x3f)|0x80); \ + } \ + (s)[(i)++]=(uint8_t)(((c)&0x3f)|0x80); \ + } \ +} +#endif diff --git a/Source/JavaScriptCore/wtf/unicode/glib/UnicodeGLib.cpp b/Source/JavaScriptCore/wtf/unicode/glib/UnicodeGLib.cpp new file mode 100644 index 000000000..a01c3ee9d --- /dev/null +++ b/Source/JavaScriptCore/wtf/unicode/glib/UnicodeGLib.cpp @@ -0,0 +1,192 @@ +/* + * Copyright (C) 2008 Jürg Billeter <j@bitron.ch> + * Copyright (C) 2008 Dominik Röttsches <dominik.roettsches@access-company.com> + * Copyright (C) 2010 Igalia S.L. + * + * This library 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 library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#include "config.h" +#include "UnicodeGLib.h" + +#include <wtf/Vector.h> +#include <wtf/unicode/UTF8.h> + +#define UTF8_IS_SURROGATE(character) (character >= 0x10000 && character <= 0x10FFFF) + +namespace WTF { +namespace Unicode { + +UChar32 foldCase(UChar32 ch) +{ + GOwnPtr<GError> gerror; + + GOwnPtr<char> utf8char; + utf8char.set(g_ucs4_to_utf8(reinterpret_cast<gunichar*>(&ch), 1, 0, 0, &gerror.outPtr())); + if (gerror) + return ch; + + GOwnPtr<char> utf8caseFolded; + utf8caseFolded.set(g_utf8_casefold(utf8char.get(), -1)); + + GOwnPtr<gunichar> ucs4Result; + ucs4Result.set(g_utf8_to_ucs4_fast(utf8caseFolded.get(), -1, 0)); + + return *ucs4Result; +} + +static int getUTF16LengthFromUTF8(const gchar* utf8String, int length) +{ + int utf16Length = 0; + const gchar* inputString = utf8String; + + while ((utf8String + length - inputString > 0) && *inputString) { + gunichar character = g_utf8_get_char(inputString); + + utf16Length += UTF8_IS_SURROGATE(character) ? 2 : 1; + inputString = g_utf8_next_char(inputString); + } + + return utf16Length; +} + +typedef gchar* (*UTF8CaseFunction)(const gchar*, gssize length); + +static int convertCase(UChar* result, int resultLength, const UChar* src, int srcLength, bool* error, UTF8CaseFunction caseFunction) +{ + *error = false; + + // Allocate a buffer big enough to hold all the characters. + Vector<char> buffer(srcLength * 3); + char* utf8Target = buffer.data(); + const UChar* utf16Source = src; + ConversionResult conversionResult = convertUTF16ToUTF8(&utf16Source, utf16Source + srcLength, &utf8Target, utf8Target + buffer.size(), true); + if (conversionResult != conversionOK) { + *error = true; + return -1; + } + buffer.shrink(utf8Target - buffer.data()); + + GOwnPtr<char> utf8Result(caseFunction(buffer.data(), buffer.size())); + long utf8ResultLength = strlen(utf8Result.get()); + + // Calculate the destination buffer size. + int realLength = getUTF16LengthFromUTF8(utf8Result.get(), utf8ResultLength); + if (realLength > resultLength) { + *error = true; + return realLength; + } + + // Convert the result to UTF-16. + UChar* utf16Target = result; + const char* utf8Source = utf8Result.get(); + conversionResult = convertUTF8ToUTF16(&utf8Source, utf8Source + utf8ResultLength, &utf16Target, utf16Target + resultLength, true); + long utf16ResultLength = utf16Target - result; + if (conversionResult != conversionOK) + *error = true; + + return utf16ResultLength <= 0 ? -1 : utf16ResultLength; +} +int foldCase(UChar* result, int resultLength, const UChar* src, int srcLength, bool* error) +{ + return convertCase(result, resultLength, src, srcLength, error, g_utf8_casefold); +} + +int toLower(UChar* result, int resultLength, const UChar* src, int srcLength, bool* error) +{ + return convertCase(result, resultLength, src, srcLength, error, g_utf8_strdown); +} + +int toUpper(UChar* result, int resultLength, const UChar* src, int srcLength, bool* error) +{ + return convertCase(result, resultLength, src, srcLength, error, g_utf8_strup); +} + +Direction direction(UChar32 c) +{ + PangoBidiType type = pango_bidi_type_for_unichar(c); + switch (type) { + case PANGO_BIDI_TYPE_L: + return LeftToRight; + case PANGO_BIDI_TYPE_R: + return RightToLeft; + case PANGO_BIDI_TYPE_AL: + return RightToLeftArabic; + case PANGO_BIDI_TYPE_LRE: + return LeftToRightEmbedding; + case PANGO_BIDI_TYPE_RLE: + return RightToLeftEmbedding; + case PANGO_BIDI_TYPE_LRO: + return LeftToRightOverride; + case PANGO_BIDI_TYPE_RLO: + return RightToLeftOverride; + case PANGO_BIDI_TYPE_PDF: + return PopDirectionalFormat; + case PANGO_BIDI_TYPE_EN: + return EuropeanNumber; + case PANGO_BIDI_TYPE_AN: + return ArabicNumber; + case PANGO_BIDI_TYPE_ES: + return EuropeanNumberSeparator; + case PANGO_BIDI_TYPE_ET: + return EuropeanNumberTerminator; + case PANGO_BIDI_TYPE_CS: + return CommonNumberSeparator; + case PANGO_BIDI_TYPE_NSM: + return NonSpacingMark; + case PANGO_BIDI_TYPE_BN: + return BoundaryNeutral; + case PANGO_BIDI_TYPE_B: + return BlockSeparator; + case PANGO_BIDI_TYPE_S: + return SegmentSeparator; + case PANGO_BIDI_TYPE_WS: + return WhiteSpaceNeutral; + default: + return OtherNeutral; + } +} + +int umemcasecmp(const UChar* a, const UChar* b, int len) +{ + GOwnPtr<char> utf8a; + GOwnPtr<char> utf8b; + + utf8a.set(g_utf16_to_utf8(a, len, 0, 0, 0)); + utf8b.set(g_utf16_to_utf8(b, len, 0, 0, 0)); + + GOwnPtr<char> foldedA; + GOwnPtr<char> foldedB; + + foldedA.set(g_utf8_casefold(utf8a.get(), -1)); + foldedB.set(g_utf8_casefold(utf8b.get(), -1)); + + // FIXME: umemcasecmp needs to mimic u_memcasecmp of icu + // from the ICU docs: + // "Compare two strings case-insensitively using full case folding. + // his is equivalent to u_strcmp(u_strFoldCase(s1, n, options), u_strFoldCase(s2, n, options))." + // + // So it looks like we don't need the full g_utf8_collate here, + // but really a bitwise comparison of casefolded unicode chars (not utf-8 bytes). + // As there is no direct equivalent to this icu function in GLib, for now + // we'll use g_utf8_collate(): + + return g_utf8_collate(foldedA.get(), foldedB.get()); +} + +} +} diff --git a/Source/JavaScriptCore/wtf/unicode/glib/UnicodeGLib.h b/Source/JavaScriptCore/wtf/unicode/glib/UnicodeGLib.h new file mode 100644 index 000000000..7bff934ba --- /dev/null +++ b/Source/JavaScriptCore/wtf/unicode/glib/UnicodeGLib.h @@ -0,0 +1,244 @@ +/* + * Copyright (C) 2006 George Staikos <staikos@kde.org> + * Copyright (C) 2006 Alexey Proskuryakov <ap@nypop.com> + * Copyright (C) 2007 Apple Computer, Inc. All rights reserved. + * Copyright (C) 2008 Jürg Billeter <j@bitron.ch> + * Copyright (C) 2008 Dominik Röttsches <dominik.roettsches@access-company.com> + * + * This library 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 library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#ifndef UnicodeGLib_h +#define UnicodeGLib_h + +#include "GOwnPtr.h" +#include "ScriptCodesFromICU.h" +#include "UnicodeMacrosFromICU.h" + +#include <glib.h> +#include <pango/pango.h> +#include <stdint.h> +#include <stdlib.h> +#include <string.h> + +typedef uint16_t UChar; +typedef int32_t UChar32; + +namespace WTF { +namespace Unicode { + +enum Direction { + LeftToRight, + RightToLeft, + EuropeanNumber, + EuropeanNumberSeparator, + EuropeanNumberTerminator, + ArabicNumber, + CommonNumberSeparator, + BlockSeparator, + SegmentSeparator, + WhiteSpaceNeutral, + OtherNeutral, + LeftToRightEmbedding, + LeftToRightOverride, + RightToLeftArabic, + RightToLeftEmbedding, + RightToLeftOverride, + PopDirectionalFormat, + NonSpacingMark, + BoundaryNeutral +}; + +enum DecompositionType { + DecompositionNone, + DecompositionCanonical, + DecompositionCompat, + DecompositionCircle, + DecompositionFinal, + DecompositionFont, + DecompositionFraction, + DecompositionInitial, + DecompositionIsolated, + DecompositionMedial, + DecompositionNarrow, + DecompositionNoBreak, + DecompositionSmall, + DecompositionSquare, + DecompositionSub, + DecompositionSuper, + DecompositionVertical, + DecompositionWide, +}; + +enum CharCategory { + NoCategory = 0, + Other_NotAssigned = U_MASK(G_UNICODE_UNASSIGNED), + Letter_Uppercase = U_MASK(G_UNICODE_UPPERCASE_LETTER), + Letter_Lowercase = U_MASK(G_UNICODE_LOWERCASE_LETTER), + Letter_Titlecase = U_MASK(G_UNICODE_TITLECASE_LETTER), + Letter_Modifier = U_MASK(G_UNICODE_MODIFIER_LETTER), + Letter_Other = U_MASK(G_UNICODE_OTHER_LETTER), + + Mark_NonSpacing = U_MASK(G_UNICODE_NON_SPACING_MARK), + Mark_Enclosing = U_MASK(G_UNICODE_ENCLOSING_MARK), + Mark_SpacingCombining = U_MASK(G_UNICODE_COMBINING_MARK), + + Number_DecimalDigit = U_MASK(G_UNICODE_DECIMAL_NUMBER), + Number_Letter = U_MASK(G_UNICODE_LETTER_NUMBER), + Number_Other = U_MASK(G_UNICODE_OTHER_NUMBER), + + Separator_Space = U_MASK(G_UNICODE_SPACE_SEPARATOR), + Separator_Line = U_MASK(G_UNICODE_LINE_SEPARATOR), + Separator_Paragraph = U_MASK(G_UNICODE_PARAGRAPH_SEPARATOR), + + Other_Control = U_MASK(G_UNICODE_CONTROL), + Other_Format = U_MASK(G_UNICODE_FORMAT), + Other_PrivateUse = U_MASK(G_UNICODE_PRIVATE_USE), + Other_Surrogate = U_MASK(G_UNICODE_SURROGATE), + + Punctuation_Dash = U_MASK(G_UNICODE_DASH_PUNCTUATION), + Punctuation_Open = U_MASK(G_UNICODE_OPEN_PUNCTUATION), + Punctuation_Close = U_MASK(G_UNICODE_CLOSE_PUNCTUATION), + Punctuation_Connector = U_MASK(G_UNICODE_CONNECT_PUNCTUATION), + Punctuation_Other = U_MASK(G_UNICODE_OTHER_PUNCTUATION), + + Symbol_Math = U_MASK(G_UNICODE_MATH_SYMBOL), + Symbol_Currency = U_MASK(G_UNICODE_CURRENCY_SYMBOL), + Symbol_Modifier = U_MASK(G_UNICODE_MODIFIER_SYMBOL), + Symbol_Other = U_MASK(G_UNICODE_OTHER_SYMBOL), + + Punctuation_InitialQuote = U_MASK(G_UNICODE_INITIAL_PUNCTUATION), + Punctuation_FinalQuote = U_MASK(G_UNICODE_FINAL_PUNCTUATION) +}; + +UChar32 foldCase(UChar32); + +int foldCase(UChar* result, int resultLength, const UChar* src, int srcLength, bool* error); + +int toLower(UChar* result, int resultLength, const UChar* src, int srcLength, bool* error); + +inline UChar32 toLower(UChar32 c) +{ + return g_unichar_tolower(c); +} + +inline UChar32 toUpper(UChar32 c) +{ + return g_unichar_toupper(c); +} + +int toUpper(UChar* result, int resultLength, const UChar* src, int srcLength, bool* error); + +inline UChar32 toTitleCase(UChar32 c) +{ + return g_unichar_totitle(c); +} + +inline bool isArabicChar(UChar32 c) +{ + return c >= 0x0600 && c <= 0x06FF; +} + +inline bool isAlphanumeric(UChar32 c) +{ + return g_unichar_isalnum(c); +} + +inline bool isFormatChar(UChar32 c) +{ + return g_unichar_type(c) == G_UNICODE_FORMAT; +} + +inline bool isSeparatorSpace(UChar32 c) +{ + return g_unichar_type(c) == G_UNICODE_SPACE_SEPARATOR; +} + +inline bool isPrintableChar(UChar32 c) +{ + return g_unichar_isprint(c); +} + +inline bool isDigit(UChar32 c) +{ + return g_unichar_isdigit(c); +} + +inline bool isPunct(UChar32 c) +{ + return g_unichar_ispunct(c); +} + +inline bool hasLineBreakingPropertyComplexContext(UChar32 c) +{ + // FIXME + return false; +} + +inline bool hasLineBreakingPropertyComplexContextOrIdeographic(UChar32 c) +{ + // FIXME + return false; +} + +inline UChar32 mirroredChar(UChar32 c) +{ + gunichar mirror = 0; + g_unichar_get_mirror_char(c, &mirror); + return mirror; +} + +inline CharCategory category(UChar32 c) +{ + if (c > 0xffff) + return NoCategory; + + return (CharCategory) U_MASK(g_unichar_type(c)); +} + +Direction direction(UChar32); + +inline bool isLower(UChar32 c) +{ + return g_unichar_islower(c); +} + +inline int digitValue(UChar32 c) +{ + return g_unichar_digit_value(c); +} + +inline uint8_t combiningClass(UChar32 c) +{ + // FIXME + // return g_unichar_combining_class(c); + return 0; +} + +inline DecompositionType decompositionType(UChar32 c) +{ + // FIXME + return DecompositionNone; +} + +int umemcasecmp(const UChar*, const UChar*, int len); + +} +} + +#endif + diff --git a/Source/JavaScriptCore/wtf/unicode/icu/CollatorICU.cpp b/Source/JavaScriptCore/wtf/unicode/icu/CollatorICU.cpp new file mode 100644 index 000000000..348693f4d --- /dev/null +++ b/Source/JavaScriptCore/wtf/unicode/icu/CollatorICU.cpp @@ -0,0 +1,149 @@ +/* + * Copyright (C) 2008 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of Apple Computer, Inc. ("Apple") 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 APPLE AND ITS 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 APPLE OR ITS 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 "config.h" +#include "Collator.h" + +#if USE(ICU_UNICODE) && !UCONFIG_NO_COLLATION + +#include "Assertions.h" +#include "Threading.h" +#include <unicode/ucol.h> +#include <string.h> + +#if OS(DARWIN) +#include "RetainPtr.h" +#include <CoreFoundation/CoreFoundation.h> +#endif + +namespace WTF { + +static UCollator* cachedCollator; +static Mutex& cachedCollatorMutex() +{ + AtomicallyInitializedStatic(Mutex&, mutex = *new Mutex); + return mutex; +} + +Collator::Collator(const char* locale) + : m_collator(0) + , m_locale(locale ? strdup(locale) : 0) + , m_lowerFirst(false) +{ +} + +PassOwnPtr<Collator> Collator::userDefault() +{ +#if OS(DARWIN) && USE(CF) + // Mac OS X doesn't set UNIX locale to match user-selected one, so ICU default doesn't work. +#if !defined(BUILDING_ON_LEOPARD) && !OS(IOS) + RetainPtr<CFLocaleRef> currentLocale(AdoptCF, CFLocaleCopyCurrent()); + CFStringRef collationOrder = (CFStringRef)CFLocaleGetValue(currentLocale.get(), kCFLocaleCollatorIdentifier); +#else + RetainPtr<CFStringRef> collationOrderRetainer(AdoptCF, (CFStringRef)CFPreferencesCopyValue(CFSTR("AppleCollationOrder"), kCFPreferencesAnyApplication, kCFPreferencesCurrentUser, kCFPreferencesAnyHost)); + CFStringRef collationOrder = collationOrderRetainer.get(); +#endif + char buf[256]; + if (!collationOrder) + return adoptPtr(new Collator("")); + CFStringGetCString(collationOrder, buf, sizeof(buf), kCFStringEncodingASCII); + return adoptPtr(new Collator(buf)); +#else + return adoptPtr(new Collator(0)); +#endif +} + +Collator::~Collator() +{ + releaseCollator(); + free(m_locale); +} + +void Collator::setOrderLowerFirst(bool lowerFirst) +{ + m_lowerFirst = lowerFirst; +} + +Collator::Result Collator::collate(const UChar* lhs, size_t lhsLength, const UChar* rhs, size_t rhsLength) const +{ + if (!m_collator) + createCollator(); + + return static_cast<Result>(ucol_strcoll(m_collator, lhs, lhsLength, rhs, rhsLength)); +} + +void Collator::createCollator() const +{ + ASSERT(!m_collator); + UErrorCode status = U_ZERO_ERROR; + + { + Locker<Mutex> lock(cachedCollatorMutex()); + if (cachedCollator) { + const char* cachedCollatorLocale = ucol_getLocaleByType(cachedCollator, ULOC_REQUESTED_LOCALE, &status); + ASSERT(U_SUCCESS(status)); + ASSERT(cachedCollatorLocale); + + UColAttributeValue cachedCollatorLowerFirst = ucol_getAttribute(cachedCollator, UCOL_CASE_FIRST, &status); + ASSERT(U_SUCCESS(status)); + + // FIXME: default locale is never matched, because ucol_getLocaleByType returns the actual one used, not 0. + if (m_locale && 0 == strcmp(cachedCollatorLocale, m_locale) + && ((UCOL_LOWER_FIRST == cachedCollatorLowerFirst && m_lowerFirst) || (UCOL_UPPER_FIRST == cachedCollatorLowerFirst && !m_lowerFirst))) { + m_collator = cachedCollator; + cachedCollator = 0; + return; + } + } + } + + m_collator = ucol_open(m_locale, &status); + if (U_FAILURE(status)) { + status = U_ZERO_ERROR; + m_collator = ucol_open("", &status); // Fallback to Unicode Collation Algorithm. + } + ASSERT(U_SUCCESS(status)); + + ucol_setAttribute(m_collator, UCOL_CASE_FIRST, m_lowerFirst ? UCOL_LOWER_FIRST : UCOL_UPPER_FIRST, &status); + ASSERT(U_SUCCESS(status)); +} + +void Collator::releaseCollator() +{ + { + Locker<Mutex> lock(cachedCollatorMutex()); + if (cachedCollator) + ucol_close(cachedCollator); + cachedCollator = m_collator; + m_collator = 0; + } +} + +} + +#endif diff --git a/Source/JavaScriptCore/wtf/unicode/icu/UnicodeIcu.h b/Source/JavaScriptCore/wtf/unicode/icu/UnicodeIcu.h new file mode 100644 index 000000000..fe524b2a2 --- /dev/null +++ b/Source/JavaScriptCore/wtf/unicode/icu/UnicodeIcu.h @@ -0,0 +1,236 @@ +/* + * Copyright (C) 2006 George Staikos <staikos@kde.org> + * Copyright (C) 2006 Alexey Proskuryakov <ap@nypop.com> + * Copyright (C) 2006, 2007, 2008, 2009 Apple Inc. All rights reserved. + * + * This library 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 library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#ifndef WTF_UNICODE_ICU_H +#define WTF_UNICODE_ICU_H + +#include <stdlib.h> +#include <unicode/uchar.h> +#include <unicode/uscript.h> +#include <unicode/ustring.h> +#include <unicode/utf16.h> + +namespace WTF { +namespace Unicode { + +enum Direction { + LeftToRight = U_LEFT_TO_RIGHT, + RightToLeft = U_RIGHT_TO_LEFT, + EuropeanNumber = U_EUROPEAN_NUMBER, + EuropeanNumberSeparator = U_EUROPEAN_NUMBER_SEPARATOR, + EuropeanNumberTerminator = U_EUROPEAN_NUMBER_TERMINATOR, + ArabicNumber = U_ARABIC_NUMBER, + CommonNumberSeparator = U_COMMON_NUMBER_SEPARATOR, + BlockSeparator = U_BLOCK_SEPARATOR, + SegmentSeparator = U_SEGMENT_SEPARATOR, + WhiteSpaceNeutral = U_WHITE_SPACE_NEUTRAL, + OtherNeutral = U_OTHER_NEUTRAL, + LeftToRightEmbedding = U_LEFT_TO_RIGHT_EMBEDDING, + LeftToRightOverride = U_LEFT_TO_RIGHT_OVERRIDE, + RightToLeftArabic = U_RIGHT_TO_LEFT_ARABIC, + RightToLeftEmbedding = U_RIGHT_TO_LEFT_EMBEDDING, + RightToLeftOverride = U_RIGHT_TO_LEFT_OVERRIDE, + PopDirectionalFormat = U_POP_DIRECTIONAL_FORMAT, + NonSpacingMark = U_DIR_NON_SPACING_MARK, + BoundaryNeutral = U_BOUNDARY_NEUTRAL +}; + +enum DecompositionType { + DecompositionNone = U_DT_NONE, + DecompositionCanonical = U_DT_CANONICAL, + DecompositionCompat = U_DT_COMPAT, + DecompositionCircle = U_DT_CIRCLE, + DecompositionFinal = U_DT_FINAL, + DecompositionFont = U_DT_FONT, + DecompositionFraction = U_DT_FRACTION, + DecompositionInitial = U_DT_INITIAL, + DecompositionIsolated = U_DT_ISOLATED, + DecompositionMedial = U_DT_MEDIAL, + DecompositionNarrow = U_DT_NARROW, + DecompositionNoBreak = U_DT_NOBREAK, + DecompositionSmall = U_DT_SMALL, + DecompositionSquare = U_DT_SQUARE, + DecompositionSub = U_DT_SUB, + DecompositionSuper = U_DT_SUPER, + DecompositionVertical = U_DT_VERTICAL, + DecompositionWide = U_DT_WIDE, +}; + +enum CharCategory { + NoCategory = 0, + Other_NotAssigned = U_MASK(U_GENERAL_OTHER_TYPES), + Letter_Uppercase = U_MASK(U_UPPERCASE_LETTER), + Letter_Lowercase = U_MASK(U_LOWERCASE_LETTER), + Letter_Titlecase = U_MASK(U_TITLECASE_LETTER), + Letter_Modifier = U_MASK(U_MODIFIER_LETTER), + Letter_Other = U_MASK(U_OTHER_LETTER), + + Mark_NonSpacing = U_MASK(U_NON_SPACING_MARK), + Mark_Enclosing = U_MASK(U_ENCLOSING_MARK), + Mark_SpacingCombining = U_MASK(U_COMBINING_SPACING_MARK), + + Number_DecimalDigit = U_MASK(U_DECIMAL_DIGIT_NUMBER), + Number_Letter = U_MASK(U_LETTER_NUMBER), + Number_Other = U_MASK(U_OTHER_NUMBER), + + Separator_Space = U_MASK(U_SPACE_SEPARATOR), + Separator_Line = U_MASK(U_LINE_SEPARATOR), + Separator_Paragraph = U_MASK(U_PARAGRAPH_SEPARATOR), + + Other_Control = U_MASK(U_CONTROL_CHAR), + Other_Format = U_MASK(U_FORMAT_CHAR), + Other_PrivateUse = U_MASK(U_PRIVATE_USE_CHAR), + Other_Surrogate = U_MASK(U_SURROGATE), + + Punctuation_Dash = U_MASK(U_DASH_PUNCTUATION), + Punctuation_Open = U_MASK(U_START_PUNCTUATION), + Punctuation_Close = U_MASK(U_END_PUNCTUATION), + Punctuation_Connector = U_MASK(U_CONNECTOR_PUNCTUATION), + Punctuation_Other = U_MASK(U_OTHER_PUNCTUATION), + + Symbol_Math = U_MASK(U_MATH_SYMBOL), + Symbol_Currency = U_MASK(U_CURRENCY_SYMBOL), + Symbol_Modifier = U_MASK(U_MODIFIER_SYMBOL), + Symbol_Other = U_MASK(U_OTHER_SYMBOL), + + Punctuation_InitialQuote = U_MASK(U_INITIAL_PUNCTUATION), + Punctuation_FinalQuote = U_MASK(U_FINAL_PUNCTUATION) +}; + +inline UChar32 foldCase(UChar32 c) +{ + return u_foldCase(c, U_FOLD_CASE_DEFAULT); +} + +inline int foldCase(UChar* result, int resultLength, const UChar* src, int srcLength, bool* error) +{ + UErrorCode status = U_ZERO_ERROR; + int realLength = u_strFoldCase(result, resultLength, src, srcLength, U_FOLD_CASE_DEFAULT, &status); + *error = !U_SUCCESS(status); + return realLength; +} + +inline int toLower(UChar* result, int resultLength, const UChar* src, int srcLength, bool* error) +{ + UErrorCode status = U_ZERO_ERROR; + int realLength = u_strToLower(result, resultLength, src, srcLength, "", &status); + *error = !!U_FAILURE(status); + return realLength; +} + +inline UChar32 toLower(UChar32 c) +{ + return u_tolower(c); +} + +inline UChar32 toUpper(UChar32 c) +{ + return u_toupper(c); +} + +inline int toUpper(UChar* result, int resultLength, const UChar* src, int srcLength, bool* error) +{ + UErrorCode status = U_ZERO_ERROR; + int realLength = u_strToUpper(result, resultLength, src, srcLength, "", &status); + *error = !!U_FAILURE(status); + return realLength; +} + +inline UChar32 toTitleCase(UChar32 c) +{ + return u_totitle(c); +} + +inline bool isArabicChar(UChar32 c) +{ + return ublock_getCode(c) == UBLOCK_ARABIC; +} + +inline bool isAlphanumeric(UChar32 c) +{ + return u_isalnum(c); +} + +inline bool isSeparatorSpace(UChar32 c) +{ + return u_charType(c) == U_SPACE_SEPARATOR; +} + +inline bool isPrintableChar(UChar32 c) +{ + return !!u_isprint(c); +} + +inline bool isPunct(UChar32 c) +{ + return !!u_ispunct(c); +} + +inline bool hasLineBreakingPropertyComplexContext(UChar32 c) +{ + return u_getIntPropertyValue(c, UCHAR_LINE_BREAK) == U_LB_COMPLEX_CONTEXT; +} + +inline bool hasLineBreakingPropertyComplexContextOrIdeographic(UChar32 c) +{ + int32_t prop = u_getIntPropertyValue(c, UCHAR_LINE_BREAK); + return prop == U_LB_COMPLEX_CONTEXT || prop == U_LB_IDEOGRAPHIC; +} + +inline UChar32 mirroredChar(UChar32 c) +{ + return u_charMirror(c); +} + +inline CharCategory category(UChar32 c) +{ + return static_cast<CharCategory>(U_GET_GC_MASK(c)); +} + +inline Direction direction(UChar32 c) +{ + return static_cast<Direction>(u_charDirection(c)); +} + +inline bool isLower(UChar32 c) +{ + return !!u_islower(c); +} + +inline uint8_t combiningClass(UChar32 c) +{ + return u_getCombiningClass(c); +} + +inline DecompositionType decompositionType(UChar32 c) +{ + return static_cast<DecompositionType>(u_getIntPropertyValue(c, UCHAR_DECOMPOSITION_TYPE)); +} + +inline int umemcasecmp(const UChar* a, const UChar* b, int len) +{ + return u_memcasecmp(a, b, len, U_FOLD_CASE_DEFAULT); +} + +} } + +#endif // WTF_UNICODE_ICU_H diff --git a/Source/JavaScriptCore/wtf/unicode/qt4/UnicodeQt4.h b/Source/JavaScriptCore/wtf/unicode/qt4/UnicodeQt4.h new file mode 100644 index 000000000..e4f39b292 --- /dev/null +++ b/Source/JavaScriptCore/wtf/unicode/qt4/UnicodeQt4.h @@ -0,0 +1,377 @@ +/* + * Copyright (C) 2006 George Staikos <staikos@kde.org> + * Copyright (C) 2006 Alexey Proskuryakov <ap@nypop.com> + * Copyright (C) 2006, 2007, 2008, 2009 Apple Inc. All rights reserved. + * + * This library 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 library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#ifndef WTF_UNICODE_QT4_H +#define WTF_UNICODE_QT4_H + +#include "ScriptCodesFromICU.h" +#include "UnicodeMacrosFromICU.h" + +#include <QChar> +#include <QString> + +#include <config.h> + +#include <stdint.h> +#if USE(ICU_UNICODE) +#include <unicode/ubrk.h> +#endif + +QT_BEGIN_NAMESPACE +namespace QUnicodeTables { + struct Properties { + ushort category : 8; + ushort line_break_class : 8; + ushort direction : 8; + ushort combiningClass :8; + ushort joining : 2; + signed short digitValue : 6; /* 5 needed */ + ushort unicodeVersion : 4; + ushort lowerCaseSpecial : 1; + ushort upperCaseSpecial : 1; + ushort titleCaseSpecial : 1; + ushort caseFoldSpecial : 1; /* currently unused */ + signed short mirrorDiff : 16; + signed short lowerCaseDiff : 16; + signed short upperCaseDiff : 16; + signed short titleCaseDiff : 16; + signed short caseFoldDiff : 16; + }; + Q_CORE_EXPORT const Properties * QT_FASTCALL properties(uint ucs4); + Q_CORE_EXPORT const Properties * QT_FASTCALL properties(ushort ucs2); +} +QT_END_NAMESPACE + +// ugly hack to make UChar compatible with JSChar in API/JSStringRef.h +#if defined(Q_OS_WIN) || (COMPILER(RVCT) && !OS(LINUX)) +typedef wchar_t UChar; +#else +typedef uint16_t UChar; +#endif + +#if !USE(ICU_UNICODE) +typedef uint32_t UChar32; +#endif + +namespace WTF { +namespace Unicode { + +enum Direction { + LeftToRight = QChar::DirL, + RightToLeft = QChar::DirR, + EuropeanNumber = QChar::DirEN, + EuropeanNumberSeparator = QChar::DirES, + EuropeanNumberTerminator = QChar::DirET, + ArabicNumber = QChar::DirAN, + CommonNumberSeparator = QChar::DirCS, + BlockSeparator = QChar::DirB, + SegmentSeparator = QChar::DirS, + WhiteSpaceNeutral = QChar::DirWS, + OtherNeutral = QChar::DirON, + LeftToRightEmbedding = QChar::DirLRE, + LeftToRightOverride = QChar::DirLRO, + RightToLeftArabic = QChar::DirAL, + RightToLeftEmbedding = QChar::DirRLE, + RightToLeftOverride = QChar::DirRLO, + PopDirectionalFormat = QChar::DirPDF, + NonSpacingMark = QChar::DirNSM, + BoundaryNeutral = QChar::DirBN +}; + +enum DecompositionType { + DecompositionNone = QChar::NoDecomposition, + DecompositionCanonical = QChar::Canonical, + DecompositionCompat = QChar::Compat, + DecompositionCircle = QChar::Circle, + DecompositionFinal = QChar::Final, + DecompositionFont = QChar::Font, + DecompositionFraction = QChar::Fraction, + DecompositionInitial = QChar::Initial, + DecompositionIsolated = QChar::Isolated, + DecompositionMedial = QChar::Medial, + DecompositionNarrow = QChar::Narrow, + DecompositionNoBreak = QChar::NoBreak, + DecompositionSmall = QChar::Small, + DecompositionSquare = QChar::Square, + DecompositionSub = QChar::Sub, + DecompositionSuper = QChar::Super, + DecompositionVertical = QChar::Vertical, + DecompositionWide = QChar::Wide +}; + +enum CharCategory { + NoCategory = 0, + Mark_NonSpacing = U_MASK(QChar::Mark_NonSpacing), + Mark_SpacingCombining = U_MASK(QChar::Mark_SpacingCombining), + Mark_Enclosing = U_MASK(QChar::Mark_Enclosing), + Number_DecimalDigit = U_MASK(QChar::Number_DecimalDigit), + Number_Letter = U_MASK(QChar::Number_Letter), + Number_Other = U_MASK(QChar::Number_Other), + Separator_Space = U_MASK(QChar::Separator_Space), + Separator_Line = U_MASK(QChar::Separator_Line), + Separator_Paragraph = U_MASK(QChar::Separator_Paragraph), + Other_Control = U_MASK(QChar::Other_Control), + Other_Format = U_MASK(QChar::Other_Format), + Other_Surrogate = U_MASK(QChar::Other_Surrogate), + Other_PrivateUse = U_MASK(QChar::Other_PrivateUse), + Other_NotAssigned = U_MASK(QChar::Other_NotAssigned), + Letter_Uppercase = U_MASK(QChar::Letter_Uppercase), + Letter_Lowercase = U_MASK(QChar::Letter_Lowercase), + Letter_Titlecase = U_MASK(QChar::Letter_Titlecase), + Letter_Modifier = U_MASK(QChar::Letter_Modifier), + Letter_Other = U_MASK(QChar::Letter_Other), + Punctuation_Connector = U_MASK(QChar::Punctuation_Connector), + Punctuation_Dash = U_MASK(QChar::Punctuation_Dash), + Punctuation_Open = U_MASK(QChar::Punctuation_Open), + Punctuation_Close = U_MASK(QChar::Punctuation_Close), + Punctuation_InitialQuote = U_MASK(QChar::Punctuation_InitialQuote), + Punctuation_FinalQuote = U_MASK(QChar::Punctuation_FinalQuote), + Punctuation_Other = U_MASK(QChar::Punctuation_Other), + Symbol_Math = U_MASK(QChar::Symbol_Math), + Symbol_Currency = U_MASK(QChar::Symbol_Currency), + Symbol_Modifier = U_MASK(QChar::Symbol_Modifier), + Symbol_Other = U_MASK(QChar::Symbol_Other) +}; + + +// FIXME: handle surrogates correctly in all methods + +inline UChar32 toLower(UChar32 ch) +{ + return QChar::toLower(uint32_t(ch)); +} + +inline int toLower(UChar* result, int resultLength, const UChar* src, int srcLength, bool* error) +{ + const UChar *e = src + srcLength; + const UChar *s = src; + UChar *r = result; + uint rindex = 0; + + // this avoids one out of bounds check in the loop + if (s < e && QChar(*s).isLowSurrogate()) { + if (r) + r[rindex] = *s++; + ++rindex; + } + + int needed = 0; + while (s < e && (rindex < uint(resultLength) || !r)) { + uint c = *s; + if (QChar(c).isLowSurrogate() && QChar(*(s - 1)).isHighSurrogate()) + c = QChar::surrogateToUcs4(*(s - 1), c); + const QUnicodeTables::Properties *prop = QUnicodeTables::properties(c); + if (prop->lowerCaseSpecial) { + QString qstring; + if (c < 0x10000) { + qstring += QChar(c); + } else { + qstring += QChar(*(s-1)); + qstring += QChar(*s); + } + qstring = qstring.toLower(); + for (int i = 0; i < qstring.length(); ++i) { + if (rindex >= uint(resultLength)) { + needed += qstring.length() - i; + break; + } + if (r) + r[rindex] = qstring.at(i).unicode(); + ++rindex; + } + } else { + if (r) + r[rindex] = *s + prop->lowerCaseDiff; + ++rindex; + } + ++s; + } + if (s < e) + needed += e - s; + *error = (needed != 0); + if (rindex < uint(resultLength)) + r[rindex] = 0; + return rindex + needed; +} + +inline UChar32 toUpper(UChar32 c) +{ + return QChar::toUpper(uint32_t(c)); +} + +inline int toUpper(UChar* result, int resultLength, const UChar* src, int srcLength, bool* error) +{ + const UChar *e = src + srcLength; + const UChar *s = src; + UChar *r = result; + int rindex = 0; + + // this avoids one out of bounds check in the loop + if (s < e && QChar(*s).isLowSurrogate()) { + if (r) + r[rindex] = *s++; + ++rindex; + } + + int needed = 0; + while (s < e && (rindex < resultLength || !r)) { + uint c = *s; + if (QChar(c).isLowSurrogate() && QChar(*(s - 1)).isHighSurrogate()) + c = QChar::surrogateToUcs4(*(s - 1), c); + const QUnicodeTables::Properties *prop = QUnicodeTables::properties(c); + if (prop->upperCaseSpecial) { + QString qstring; + if (c < 0x10000) { + qstring += QChar(c); + } else { + qstring += QChar(*(s-1)); + qstring += QChar(*s); + } + qstring = qstring.toUpper(); + for (int i = 0; i < qstring.length(); ++i) { + if (rindex >= resultLength) { + needed += qstring.length() - i; + break; + } + if (r) + r[rindex] = qstring.at(i).unicode(); + ++rindex; + } + } else { + if (r) + r[rindex] = *s + prop->upperCaseDiff; + ++rindex; + } + ++s; + } + if (s < e) + needed += e - s; + *error = (needed != 0); + if (rindex < resultLength) + r[rindex] = 0; + return rindex + needed; +} + +inline int toTitleCase(UChar32 c) +{ + return QChar::toTitleCase(uint32_t(c)); +} + +inline UChar32 foldCase(UChar32 c) +{ + return QChar::toCaseFolded(uint32_t(c)); +} + +inline int foldCase(UChar* result, int resultLength, const UChar* src, int srcLength, bool* error) +{ + // FIXME: handle special casing. Easiest with some low level API in Qt + *error = false; + if (resultLength < srcLength) { + *error = true; + return srcLength; + } + for (int i = 0; i < srcLength; ++i) + result[i] = QChar::toCaseFolded(ushort(src[i])); + return srcLength; +} + +inline bool isArabicChar(UChar32 c) +{ + return c >= 0x0600 && c <= 0x06FF; +} + +inline bool isPrintableChar(UChar32 c) +{ + const uint test = U_MASK(QChar::Other_Control) | + U_MASK(QChar::Other_NotAssigned); + return !(U_MASK(QChar::category(uint32_t(c))) & test); +} + +inline bool isSeparatorSpace(UChar32 c) +{ + return QChar::category(uint32_t(c)) == QChar::Separator_Space; +} + +inline bool isPunct(UChar32 c) +{ + const uint test = U_MASK(QChar::Punctuation_Connector) | + U_MASK(QChar::Punctuation_Dash) | + U_MASK(QChar::Punctuation_Open) | + U_MASK(QChar::Punctuation_Close) | + U_MASK(QChar::Punctuation_InitialQuote) | + U_MASK(QChar::Punctuation_FinalQuote) | + U_MASK(QChar::Punctuation_Other); + return U_MASK(QChar::category(uint32_t(c))) & test; +} + +inline bool isLower(UChar32 c) +{ + return QChar::category(uint32_t(c)) == QChar::Letter_Lowercase; +} + +inline bool hasLineBreakingPropertyComplexContext(UChar32) +{ + // FIXME: Implement this to return whether the character has line breaking property SA (Complex Context). + return false; +} + +inline UChar32 mirroredChar(UChar32 c) +{ + return QChar::mirroredChar(uint32_t(c)); +} + +inline uint8_t combiningClass(UChar32 c) +{ + return QChar::combiningClass(uint32_t(c)); +} + +inline DecompositionType decompositionType(UChar32 c) +{ + return (DecompositionType)QChar::decompositionTag(c); +} + +inline int umemcasecmp(const UChar* a, const UChar* b, int len) +{ + // handle surrogates correctly + for (int i = 0; i < len; ++i) { + uint c1 = QChar::toCaseFolded(ushort(a[i])); + uint c2 = QChar::toCaseFolded(ushort(b[i])); + if (c1 != c2) + return c1 - c2; + } + return 0; +} + +inline Direction direction(UChar32 c) +{ + return (Direction)QChar::direction(uint32_t(c)); +} + +inline CharCategory category(UChar32 c) +{ + return (CharCategory) U_MASK(QChar::category(uint32_t(c))); +} + +} // namespace Unicode +} // namespace WTF + +#endif // WTF_UNICODE_QT4_H diff --git a/Source/JavaScriptCore/wtf/unicode/wince/UnicodeWinCE.cpp b/Source/JavaScriptCore/wtf/unicode/wince/UnicodeWinCE.cpp new file mode 100644 index 000000000..96dac7d40 --- /dev/null +++ b/Source/JavaScriptCore/wtf/unicode/wince/UnicodeWinCE.cpp @@ -0,0 +1,181 @@ +/* + * Copyright (C) 2006 George Staikos <staikos@kde.org> + * Copyright (C) 2006 Alexey Proskuryakov <ap@nypop.com> + * Copyright (C) 2007-2009 Torch Mobile, Inc. + * + * This library 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 library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "config.h" +#include "UnicodeWinCE.h" + +#include <wchar.h> + +namespace WTF { +namespace Unicode { + +UChar toLower(UChar c) +{ + return towlower(c); +} + +UChar toUpper(UChar c) +{ + return towupper(c); +} + +UChar foldCase(UChar c) +{ + return towlower(c); +} + +bool isPrintableChar(UChar c) +{ + return !!iswprint(c); +} + +bool isSpace(UChar c) +{ + return !!iswspace(c); +} + +bool isLetter(UChar c) +{ + return !!iswalpha(c); +} + +bool isUpper(UChar c) +{ + return !!iswupper(c); +} + +bool isLower(UChar c) +{ + return !!iswlower(c); +} + +bool isDigit(UChar c) +{ + return !!iswdigit(c); +} + +bool isPunct(UChar c) +{ + return !!iswpunct(c); +} + +bool isAlphanumeric(UChar c) +{ + return !!iswalnum(c); +} + +int toLower(UChar* result, int resultLength, const UChar* source, int sourceLength, bool* isError) +{ + const UChar* sourceIterator = source; + const UChar* sourceEnd = source + sourceLength; + UChar* resultIterator = result; + UChar* resultEnd = result + resultLength; + + int remainingCharacters = 0; + if (sourceLength <= resultLength) + while (sourceIterator < sourceEnd) + *resultIterator++ = towlower(*sourceIterator++); + else + while (resultIterator < resultEnd) + *resultIterator++ = towlower(*sourceIterator++); + + if (sourceIterator < sourceEnd) + remainingCharacters += sourceEnd - sourceIterator; + *isError = !!remainingCharacters; + if (resultIterator < resultEnd) + *resultIterator = 0; + + return (resultIterator - result) + remainingCharacters; +} + +int toUpper(UChar* result, int resultLength, const UChar* source, int sourceLength, bool* isError) +{ + const UChar* sourceIterator = source; + const UChar* sourceEnd = source + sourceLength; + UChar* resultIterator = result; + UChar* resultEnd = result + resultLength; + + int remainingCharacters = 0; + if (sourceLength <= resultLength) + while (sourceIterator < sourceEnd) + *resultIterator++ = towupper(*sourceIterator++); + else + while (resultIterator < resultEnd) + *resultIterator++ = towupper(*sourceIterator++); + + if (sourceIterator < sourceEnd) + remainingCharacters += sourceEnd - sourceIterator; + *isError = !!remainingCharacters; + if (resultIterator < resultEnd) + *resultIterator = 0; + + return (resultIterator - result) + remainingCharacters; +} + +int foldCase(UChar* result, int resultLength, const UChar* source, int sourceLength, bool* isError) +{ + *isError = false; + if (resultLength < sourceLength) { + *isError = true; + return sourceLength; + } + for (int i = 0; i < sourceLength; ++i) + result[i] = foldCase(source[i]); + return sourceLength; +} + +UChar toTitleCase(UChar c) +{ + return towupper(c); +} + +Direction direction(UChar32 c) +{ + return static_cast<Direction>(UnicodeCE::direction(c)); +} + +CharCategory category(unsigned int c) +{ + return static_cast<CharCategory>(TO_MASK((__int8) UnicodeCE::category(c))); +} + +DecompositionType decompositionType(UChar32 c) +{ + return static_cast<DecompositionType>(UnicodeCE::decompositionType(c)); +} + +unsigned char combiningClass(UChar32 c) +{ + return UnicodeCE::combiningClass(c); +} + +UChar mirroredChar(UChar32 c) +{ + return UnicodeCE::mirroredChar(c); +} + +int digitValue(UChar c) +{ + return UnicodeCE::digitValue(c); +} + +} // namespace Unicode +} // namespace WTF diff --git a/Source/JavaScriptCore/wtf/unicode/wince/UnicodeWinCE.h b/Source/JavaScriptCore/wtf/unicode/wince/UnicodeWinCE.h new file mode 100644 index 000000000..af6ff0748 --- /dev/null +++ b/Source/JavaScriptCore/wtf/unicode/wince/UnicodeWinCE.h @@ -0,0 +1,178 @@ +/* + * Copyright (C) 2006 George Staikos <staikos@kde.org> + * Copyright (C) 2006 Alexey Proskuryakov <ap@nypop.com> + * Copyright (C) 2007 Apple Computer, Inc. All rights reserved. + * Copyright (C) 2007-2009 Torch Mobile, Inc. + * + * This library 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 library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#ifndef WTF_UnicodeWinCE_h +#define WTF_UnicodeWinCE_h + +#include "ScriptCodesFromICU.h" +#include "UnicodeMacrosFromICU.h" + +#include "ce_unicode.h" + +#define TO_MASK(x) (1 << (x)) + +namespace WTF { +namespace Unicode { + +enum Direction { + LeftToRight = UnicodeCE::U_LEFT_TO_RIGHT, + RightToLeft = UnicodeCE::U_RIGHT_TO_LEFT, + EuropeanNumber = UnicodeCE::U_EUROPEAN_NUMBER, + EuropeanNumberSeparator = UnicodeCE::U_EUROPEAN_NUMBER_SEPARATOR, + EuropeanNumberTerminator = UnicodeCE::U_EUROPEAN_NUMBER_TERMINATOR, + ArabicNumber = UnicodeCE::U_ARABIC_NUMBER, + CommonNumberSeparator = UnicodeCE::U_COMMON_NUMBER_SEPARATOR, + BlockSeparator = UnicodeCE::U_BLOCK_SEPARATOR, + SegmentSeparator = UnicodeCE::U_SEGMENT_SEPARATOR, + WhiteSpaceNeutral = UnicodeCE::U_WHITE_SPACE_NEUTRAL, + OtherNeutral = UnicodeCE::U_OTHER_NEUTRAL, + LeftToRightEmbedding = UnicodeCE::U_LEFT_TO_RIGHT_EMBEDDING, + LeftToRightOverride = UnicodeCE::U_LEFT_TO_RIGHT_OVERRIDE, + RightToLeftArabic = UnicodeCE::U_RIGHT_TO_LEFT_ARABIC, + RightToLeftEmbedding = UnicodeCE::U_RIGHT_TO_LEFT_EMBEDDING, + RightToLeftOverride = UnicodeCE::U_RIGHT_TO_LEFT_OVERRIDE, + PopDirectionalFormat = UnicodeCE::U_POP_DIRECTIONAL_FORMAT, + NonSpacingMark = UnicodeCE::U_DIR_NON_SPACING_MARK, + BoundaryNeutral = UnicodeCE::U_BOUNDARY_NEUTRAL +}; + +enum DecompositionType { + DecompositionNone = UnicodeCE::U_DT_NONE, + DecompositionCanonical = UnicodeCE::U_DT_CANONICAL, + DecompositionCompat = UnicodeCE::U_DT_COMPAT, + DecompositionCircle = UnicodeCE::U_DT_CIRCLE, + DecompositionFinal = UnicodeCE::U_DT_FINAL, + DecompositionFont = UnicodeCE::U_DT_FONT, + DecompositionFraction = UnicodeCE::U_DT_FRACTION, + DecompositionInitial = UnicodeCE::U_DT_INITIAL, + DecompositionIsolated = UnicodeCE::U_DT_ISOLATED, + DecompositionMedial = UnicodeCE::U_DT_MEDIAL, + DecompositionNarrow = UnicodeCE::U_DT_NARROW, + DecompositionNoBreak = UnicodeCE::U_DT_NOBREAK, + DecompositionSmall = UnicodeCE::U_DT_SMALL, + DecompositionSquare = UnicodeCE::U_DT_SQUARE, + DecompositionSub = UnicodeCE::U_DT_SUB, + DecompositionSuper = UnicodeCE::U_DT_SUPER, + DecompositionVertical = UnicodeCE::U_DT_VERTICAL, + DecompositionWide = UnicodeCE::U_DT_WIDE +}; + +enum CharCategory { + NoCategory = 0, + Other_NotAssigned = TO_MASK(UnicodeCE::U_GENERAL_OTHER_TYPES), + Letter_Uppercase = TO_MASK(UnicodeCE::U_UPPERCASE_LETTER), + Letter_Lowercase = TO_MASK(UnicodeCE::U_LOWERCASE_LETTER), + Letter_Titlecase = TO_MASK(UnicodeCE::U_TITLECASE_LETTER), + Letter_Modifier = TO_MASK(UnicodeCE::U_MODIFIER_LETTER), + Letter_Other = TO_MASK(UnicodeCE::U_OTHER_LETTER), + + Mark_NonSpacing = TO_MASK(UnicodeCE::U_NON_SPACING_MARK), + Mark_Enclosing = TO_MASK(UnicodeCE::U_ENCLOSING_MARK), + Mark_SpacingCombining = TO_MASK(UnicodeCE::U_COMBINING_SPACING_MARK), + + Number_DecimalDigit = TO_MASK(UnicodeCE::U_DECIMAL_DIGIT_NUMBER), + Number_Letter = TO_MASK(UnicodeCE::U_LETTER_NUMBER), + Number_Other = TO_MASK(UnicodeCE::U_OTHER_NUMBER), + + Separator_Space = TO_MASK(UnicodeCE::U_SPACE_SEPARATOR), + Separator_Line = TO_MASK(UnicodeCE::U_LINE_SEPARATOR), + Separator_Paragraph = TO_MASK(UnicodeCE::U_PARAGRAPH_SEPARATOR), + + Other_Control = TO_MASK(UnicodeCE::U_CONTROL_CHAR), + Other_Format = TO_MASK(UnicodeCE::U_FORMAT_CHAR), + Other_PrivateUse = TO_MASK(UnicodeCE::U_PRIVATE_USE_CHAR), + Other_Surrogate = TO_MASK(UnicodeCE::U_SURROGATE), + + Punctuation_Dash = TO_MASK(UnicodeCE::U_DASH_PUNCTUATION), + Punctuation_Open = TO_MASK(UnicodeCE::U_START_PUNCTUATION), + Punctuation_Close = TO_MASK(UnicodeCE::U_END_PUNCTUATION), + Punctuation_Connector = TO_MASK(UnicodeCE::U_CONNECTOR_PUNCTUATION), + Punctuation_Other = TO_MASK(UnicodeCE::U_OTHER_PUNCTUATION), + + Symbol_Math = TO_MASK(UnicodeCE::U_MATH_SYMBOL), + Symbol_Currency = TO_MASK(UnicodeCE::U_CURRENCY_SYMBOL), + Symbol_Modifier = TO_MASK(UnicodeCE::U_MODIFIER_SYMBOL), + Symbol_Other = TO_MASK(UnicodeCE::U_OTHER_SYMBOL), + + Punctuation_InitialQuote = TO_MASK(UnicodeCE::U_INITIAL_PUNCTUATION), + Punctuation_FinalQuote = TO_MASK(UnicodeCE::U_FINAL_PUNCTUATION) +}; + +CharCategory category(unsigned int); + +bool isSpace(UChar); +bool isLetter(UChar); +bool isPrintableChar(UChar); +bool isUpper(UChar); +bool isLower(UChar); +bool isPunct(UChar); +bool isDigit(UChar); +bool isAlphanumeric(UChar); +inline bool isSeparatorSpace(UChar c) { return category(c) == Separator_Space; } +inline bool isHighSurrogate(UChar c) { return (c & 0xfc00) == 0xd800; } +inline bool isLowSurrogate(UChar c) { return (c & 0xfc00) == 0xdc00; } + +UChar toLower(UChar); +UChar toUpper(UChar); +UChar foldCase(UChar); +UChar toTitleCase(UChar); +int toLower(UChar* result, int resultLength, const UChar* source, int sourceLength, bool* isError); +int toUpper(UChar* result, int resultLength, const UChar* source, int sourceLength, bool* isError); +int foldCase(UChar* result, int resultLength, const UChar* source, int sourceLength, bool* isError); + +int digitValue(UChar); + +UChar mirroredChar(UChar32); +unsigned char combiningClass(UChar32); +DecompositionType decompositionType(UChar32); +Direction direction(UChar32); +inline bool isArabicChar(UChar32 c) +{ + return c >= 0x0600 && c <= 0x06FF; +} + +inline bool hasLineBreakingPropertyComplexContext(UChar32) +{ + return false; // FIXME: implement! +} + +inline int umemcasecmp(const UChar* a, const UChar* b, int len) +{ + for (int i = 0; i < len; ++i) { + UChar c1 = foldCase(a[i]); + UChar c2 = foldCase(b[i]); + if (c1 != c2) + return c1 - c2; + } + return 0; +} + +inline UChar32 surrogateToUcs4(UChar high, UChar low) +{ + return (UChar32(high) << 10) + low - 0x35fdc00; +} + +} // namespace Unicode +} // namespace WTF + +#endif // WTF_UnicodeWinCE_h diff --git a/Source/JavaScriptCore/wtf/win/MainThreadWin.cpp b/Source/JavaScriptCore/wtf/win/MainThreadWin.cpp new file mode 100644 index 000000000..ee3a27377 --- /dev/null +++ b/Source/JavaScriptCore/wtf/win/MainThreadWin.cpp @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2007, 2008 Apple Inc. All rights reserved. + * Copyright (C) 2009 Torch Mobile Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of Apple Computer, Inc. ("Apple") 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 APPLE AND ITS 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 APPLE OR ITS 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 "config.h" +#include "MainThread.h" + +#include "Assertions.h" +#include "Threading.h" +#if !OS(WINCE) +#include <windows.h> +#endif + +namespace WTF { + +static HWND threadingWindowHandle; +static UINT threadingFiredMessage; +const LPCWSTR kThreadingWindowClassName = L"ThreadingWindowClass"; + +LRESULT CALLBACK ThreadingWindowWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + if (message == threadingFiredMessage) + dispatchFunctionsFromMainThread(); + else + return DefWindowProc(hWnd, message, wParam, lParam); + return 0; +} + +void initializeMainThreadPlatform() +{ + if (threadingWindowHandle) + return; + + HWND hWndParent = 0; +#if OS(WINCE) + WNDCLASS wcex; + memset(&wcex, 0, sizeof(WNDCLASS)); +#else + WNDCLASSEX wcex; + memset(&wcex, 0, sizeof(WNDCLASSEX)); + wcex.cbSize = sizeof(WNDCLASSEX); +#endif + wcex.lpfnWndProc = ThreadingWindowWndProc; + wcex.lpszClassName = kThreadingWindowClassName; +#if OS(WINCE) + RegisterClass(&wcex); +#else + RegisterClassEx(&wcex); + hWndParent = HWND_MESSAGE; +#endif + + threadingWindowHandle = CreateWindow(kThreadingWindowClassName, 0, 0, + CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, hWndParent, 0, 0, 0); + threadingFiredMessage = RegisterWindowMessage(L"com.apple.WebKit.MainThreadFired"); + + initializeCurrentThreadInternal("Main Thread"); +} + +void scheduleDispatchFunctionsOnMainThread() +{ + ASSERT(threadingWindowHandle); + PostMessage(threadingWindowHandle, threadingFiredMessage, 0, 0); +} + +} // namespace WTF diff --git a/Source/JavaScriptCore/wtf/win/OwnPtrWin.cpp b/Source/JavaScriptCore/wtf/win/OwnPtrWin.cpp new file mode 100644 index 000000000..67a32ff77 --- /dev/null +++ b/Source/JavaScriptCore/wtf/win/OwnPtrWin.cpp @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2007 Apple Inc. All rights reserved. + * Copyright (C) 2008, 2009 Torch Mobile, Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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 APPLE COMPUTER, INC. ``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 APPLE COMPUTER, INC. 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 "config.h" +#include "OwnPtr.h" + +#include <windows.h> + +namespace WTF { + +void deleteOwnedPtr(HBITMAP ptr) +{ + if (ptr) + DeleteObject(ptr); +} + +void deleteOwnedPtr(HBRUSH ptr) +{ + if (ptr) + DeleteObject(ptr); +} + +void deleteOwnedPtr(HDC ptr) +{ + if (ptr) + DeleteDC(ptr); +} + +void deleteOwnedPtr(HFONT ptr) +{ + if (ptr) + DeleteObject(ptr); +} + +void deleteOwnedPtr(HPALETTE ptr) +{ + if (ptr) + DeleteObject(ptr); +} + +void deleteOwnedPtr(HPEN ptr) +{ + if (ptr) + DeleteObject(ptr); +} + +void deleteOwnedPtr(HRGN ptr) +{ + if (ptr) + DeleteObject(ptr); +} + +} diff --git a/Source/JavaScriptCore/wtf/wince/FastMallocWinCE.h b/Source/JavaScriptCore/wtf/wince/FastMallocWinCE.h new file mode 100644 index 000000000..d91a5f2c9 --- /dev/null +++ b/Source/JavaScriptCore/wtf/wince/FastMallocWinCE.h @@ -0,0 +1,175 @@ +/* + * Copyright (C) 2005, 2006, 2007, 2008 Apple Inc. All rights reserved. + * Copyright (C) 2007-2009 Torch Mobile, Inc. All rights reserved + * + * This library 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 library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#ifndef WTF_FastMallocWinCE_h +#define WTF_FastMallocWinCE_h + +#include <new.h> + +#ifdef __cplusplus +#include <new> +#include "MemoryManager.h" +extern "C" { +#endif + +void* fastMalloc(size_t n); +void* fastCalloc(size_t n_elements, size_t element_size); +void fastFree(void* p); +void* fastRealloc(void* p, size_t n); +void* fastZeroedMalloc(size_t n); +// These functions return 0 if an allocation fails. +void* tryFastMalloc(size_t n); +void* tryFastZeroedMalloc(size_t n); +void* tryFastCalloc(size_t n_elements, size_t element_size); +void* tryFastRealloc(void* p, size_t n); +char* fastStrDup(const char*); + +#ifndef NDEBUG +void fastMallocForbid(); +void fastMallocAllow(); +#endif + +#if !defined(USE_SYSTEM_MALLOC) || !USE_SYSTEM_MALLOC + +#define malloc(n) fastMalloc(n) +#define calloc(n_elements, element_size) fastCalloc(n_elements, element_size) +#define realloc(p, n) fastRealloc(p, n) +#define free(p) fastFree(p) +#define strdup(p) fastStrDup(p) + +#else + +#define strdup(p) _strdup(p) + +#endif + +#ifdef __cplusplus +} +#endif + +#ifdef __cplusplus +#if !defined(USE_SYSTEM_MALLOC) || !USE_SYSTEM_MALLOC +static inline void* __cdecl operator new(size_t s) { return fastMalloc(s); } +static inline void __cdecl operator delete(void* p) { fastFree(p); } +static inline void* __cdecl operator new[](size_t s) { return fastMalloc(s); } +static inline void __cdecl operator delete[](void* p) { fastFree(p); } +static inline void* operator new(size_t s, const std::nothrow_t&) throw() { return fastMalloc(s); } +static inline void operator delete(void* p, const std::nothrow_t&) throw() { fastFree(p); } +static inline void* operator new[](size_t s, const std::nothrow_t&) throw() { return fastMalloc(s); } +static inline void operator delete[](void* p, const std::nothrow_t&) throw() { fastFree(p); } +#endif + +namespace WTF { + // This defines a type which holds an unsigned integer and is the same + // size as the minimally aligned memory allocation. + typedef unsigned long long AllocAlignmentInteger; + + namespace Internal { + enum AllocType { // Start with an unusual number instead of zero, because zero is common. + AllocTypeMalloc = 0x375d6750, // Encompasses fastMalloc, fastZeroedMalloc, fastCalloc, fastRealloc. + AllocTypeClassNew, // Encompasses class operator new from FastAllocBase. + AllocTypeClassNewArray, // Encompasses class operator new[] from FastAllocBase. + AllocTypeFastNew, // Encompasses fastNew. + AllocTypeFastNewArray, // Encompasses fastNewArray. + AllocTypeNew, // Encompasses global operator new. + AllocTypeNewArray // Encompasses global operator new[]. + }; + } + + +#if ENABLE(FAST_MALLOC_MATCH_VALIDATION) + + // Malloc validation is a scheme whereby a tag is attached to an + // allocation which identifies how it was originally allocated. + // This allows us to verify that the freeing operation matches the + // allocation operation. If memory is allocated with operator new[] + // but freed with free or delete, this system would detect that. + // In the implementation here, the tag is an integer prepended to + // the allocation memory which is assigned one of the AllocType + // enumeration values. An alternative implementation of this + // scheme could store the tag somewhere else or ignore it. + // Users of FastMalloc don't need to know or care how this tagging + // is implemented. + + namespace Internal { + + // Return the AllocType tag associated with the allocated block p. + inline AllocType fastMallocMatchValidationType(const void* p) + { + const AllocAlignmentInteger* type = static_cast<const AllocAlignmentInteger*>(p) - 1; + return static_cast<AllocType>(*type); + } + + // Return the address of the AllocType tag associated with the allocated block p. + inline AllocAlignmentInteger* fastMallocMatchValidationValue(void* p) + { + return reinterpret_cast<AllocAlignmentInteger*>(static_cast<char*>(p) - sizeof(AllocAlignmentInteger)); + } + + // Set the AllocType tag to be associaged with the allocated block p. + inline void setFastMallocMatchValidationType(void* p, AllocType allocType) + { + AllocAlignmentInteger* type = static_cast<AllocAlignmentInteger*>(p) - 1; + *type = static_cast<AllocAlignmentInteger>(allocType); + } + + // Handle a detected alloc/free mismatch. By default this calls CRASH(). + void fastMallocMatchFailed(void* p); + + } // namespace Internal + + // This is a higher level function which is used by FastMalloc-using code. + inline void fastMallocMatchValidateMalloc(void* p, Internal::AllocType allocType) + { + if (!p) + return; + + Internal::setFastMallocMatchValidationType(p, allocType); + } + + // This is a higher level function which is used by FastMalloc-using code. + inline void fastMallocMatchValidateFree(void* p, Internal::AllocType allocType) + { + if (!p) + return; + + if (Internal::fastMallocMatchValidationType(p) != allocType) + Internal::fastMallocMatchFailed(p); + Internal::setFastMallocMatchValidationType(p, Internal::AllocTypeMalloc); // Set it to this so that fastFree thinks it's OK. + } + +#else + + inline void fastMallocMatchValidateMalloc(void*, Internal::AllocType) + { + } + + inline void fastMallocMatchValidateFree(void*, Internal::AllocType) + { + } + +#endif + +} // namespace WTF + +#endif + +#endif // WTF_FastMallocWinCE_h diff --git a/Source/JavaScriptCore/wtf/wince/MemoryManager.cpp b/Source/JavaScriptCore/wtf/wince/MemoryManager.cpp new file mode 100644 index 000000000..81d4f805b --- /dev/null +++ b/Source/JavaScriptCore/wtf/wince/MemoryManager.cpp @@ -0,0 +1,171 @@ +/* + * Copyright (C) 2008-2009 Torch Mobile Inc. + * + * This library 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 library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "config.h" +#include "MemoryManager.h" + +#undef malloc +#undef calloc +#undef realloc +#undef free +#undef strdup +#undef _strdup +#undef VirtualAlloc +#undef VirtualFree + +#include <malloc.h> +#include <windows.h> + +namespace WTF { + +MemoryManager* memoryManager() +{ + static MemoryManager mm; + return &mm; +} + +MemoryManager::MemoryManager() +: m_allocationCanFail(false) +{ +} + +MemoryManager::~MemoryManager() +{ +} + +HBITMAP MemoryManager::createCompatibleBitmap(HDC hdc, int width, int height) +{ + return ::CreateCompatibleBitmap(hdc, width, height); +} + +HBITMAP MemoryManager::createDIBSection(const BITMAPINFO* pbmi, void** ppvBits) +{ + return ::CreateDIBSection(0, pbmi, DIB_RGB_COLORS, ppvBits, 0, 0); +} + +void* MemoryManager::m_malloc(size_t size) +{ + return malloc(size); +} + +void* MemoryManager::m_calloc(size_t num, size_t size) +{ + return calloc(num, size); +} + +void* MemoryManager::m_realloc(void* p, size_t size) +{ + return realloc(p, size); +} + +void MemoryManager::m_free(void* p) +{ + return free(p); +} + +bool MemoryManager::resizeMemory(void*, size_t) +{ + return false; +} + +void* MemoryManager::allocate64kBlock() +{ + return VirtualAlloc(0, 65536, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE); +} + +void MemoryManager::free64kBlock(void* p) +{ + VirtualFree(p, 65536, MEM_RELEASE); +} + +bool MemoryManager::onIdle(DWORD& timeLimitMs) +{ + return false; +} + +LPVOID MemoryManager::virtualAlloc(LPVOID lpAddress, DWORD dwSize, DWORD flAllocationType, DWORD flProtect) +{ + return ::VirtualAlloc(lpAddress, dwSize, flAllocationType, flProtect); +} + +BOOL MemoryManager::virtualFree(LPVOID lpAddress, DWORD dwSize, DWORD dwFreeType) +{ + return ::VirtualFree(lpAddress, dwSize, dwFreeType); +} + + +#if defined(USE_SYSTEM_MALLOC) && USE_SYSTEM_MALLOC + +void *fastMalloc(size_t n) { return malloc(n); } +void *fastCalloc(size_t n_elements, size_t element_size) { return calloc(n_elements, element_size); } +void fastFree(void* p) { return free(p); } +void *fastRealloc(void* p, size_t n) { return realloc(p, n); } + +#else + +void *fastMalloc(size_t n) { return MemoryManager::m_malloc(n); } +void *fastCalloc(size_t n_elements, size_t element_size) { return MemoryManager::m_calloc(n_elements, element_size); } +void fastFree(void* p) { return MemoryManager::m_free(p); } +void *fastRealloc(void* p, size_t n) { return MemoryManager::m_realloc(p, n); } + +#endif + +#ifndef NDEBUG +void fastMallocForbid() {} +void fastMallocAllow() {} +#endif + +void* fastZeroedMalloc(size_t n) +{ + void* p = fastMalloc(n); + if (p) + memset(p, 0, n); + return p; +} + +TryMallocReturnValue tryFastMalloc(size_t n) +{ + MemoryAllocationCanFail canFail; + return fastMalloc(n); +} + +TryMallocReturnValue tryFastZeroedMalloc(size_t n) +{ + MemoryAllocationCanFail canFail; + return fastZeroedMalloc(n); +} + +TryMallocReturnValue tryFastCalloc(size_t n_elements, size_t element_size) +{ + MemoryAllocationCanFail canFail; + return fastCalloc(n_elements, element_size); +} + +TryMallocReturnValue tryFastRealloc(void* p, size_t n) +{ + MemoryAllocationCanFail canFail; + return fastRealloc(p, n); +} + +char* fastStrDup(const char* str) +{ + return _strdup(str); +} + +}
\ No newline at end of file diff --git a/Source/JavaScriptCore/wtf/wince/MemoryManager.h b/Source/JavaScriptCore/wtf/wince/MemoryManager.h new file mode 100644 index 000000000..f405612df --- /dev/null +++ b/Source/JavaScriptCore/wtf/wince/MemoryManager.h @@ -0,0 +1,80 @@ +/* + * Copyright (C) 2008-2009 Torch Mobile Inc. + * + * This library 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 library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#pragma once + +#include <winbase.h> + +typedef struct HBITMAP__* HBITMAP; +typedef struct HDC__* HDC; +typedef void *HANDLE; +typedef struct tagBITMAPINFO BITMAPINFO; + +namespace WTF { + + class MemoryManager { + public: + MemoryManager(); + ~MemoryManager(); + + bool allocationCanFail() const { return m_allocationCanFail; } + void setAllocationCanFail(bool c) { m_allocationCanFail = c; } + + static HBITMAP createCompatibleBitmap(HDC hdc, int width, int height); + static HBITMAP createDIBSection(const BITMAPINFO* pbmi, void** ppvBits); + static void* m_malloc(size_t size); + static void* m_calloc(size_t num, size_t size); + static void* m_realloc(void* p, size_t size); + static void m_free(void*); + static bool resizeMemory(void* p, size_t newSize); + static void* allocate64kBlock(); + static void free64kBlock(void*); + static bool onIdle(DWORD& timeLimitMs); + static LPVOID virtualAlloc(LPVOID lpAddress, DWORD dwSize, DWORD flAllocationType, DWORD flProtect); + static BOOL virtualFree(LPVOID lpAddress, DWORD dwSize, DWORD dwFreeType); + + private: + friend MemoryManager* memoryManager(); + + bool m_allocationCanFail; + }; + + MemoryManager* memoryManager(); + + class MemoryAllocationCanFail { + public: + MemoryAllocationCanFail() : m_old(memoryManager()->allocationCanFail()) { memoryManager()->setAllocationCanFail(true); } + ~MemoryAllocationCanFail() { memoryManager()->setAllocationCanFail(m_old); } + private: + bool m_old; + }; + + class MemoryAllocationCannotFail { + public: + MemoryAllocationCannotFail() : m_old(memoryManager()->allocationCanFail()) { memoryManager()->setAllocationCanFail(false); } + ~MemoryAllocationCannotFail() { memoryManager()->setAllocationCanFail(m_old); } + private: + bool m_old; + }; +} + +using WTF::MemoryManager; +using WTF::memoryManager; +using WTF::MemoryAllocationCanFail; +using WTF::MemoryAllocationCannotFail; diff --git a/Source/JavaScriptCore/wtf/wtf.pri b/Source/JavaScriptCore/wtf/wtf.pri new file mode 100644 index 000000000..2a3c609d5 --- /dev/null +++ b/Source/JavaScriptCore/wtf/wtf.pri @@ -0,0 +1,44 @@ +# ------------------------------------------------------------------- +# This file contains shared rules used both when building WTF itself +# and for targets that depend in some way on WTF. +# +# See 'Tools/qmake/README' for an overview of the build system +# ------------------------------------------------------------------- + +load(features) + +SOURCE_DIR = $${ROOT_WEBKIT_DIR}/Source/JavaScriptCore/wtf + +INCLUDEPATH += \ + $$SOURCE_DIR/.. \ + $$SOURCE_DIR \ + $$SOURCE_DIR/gobject \ + $$SOURCE_DIR/qt \ + $$SOURCE_DIR/unicode + +VPATH += $$SOURCE_DIR + +contains(CONFIG, use_system_icu) { + DEFINES += WTF_USE_ICU_UNICODE=1 + DEFINES -= WTF_USE_QT4_UNICODE + LIBS += -licuuc -licui18n +} else { + DEFINES += WTF_USE_QT4_UNICODE=1 + DEFINES -= WTF_USE_ICU_UNICODE +} + +v8 { + !haveQt(5): error("To build QtWebKit+V8 you need to use Qt 5") + DEFINES *= WTF_USE_V8=1 + INCLUDEPATH += $${ROOT_WEBKIT_DIR}/Source/WebKit/qt/v8/ForwardingHeaders + QT += v8-private declarative +} + +linux-*:!contains(DEFINES, USE_QTMULTIMEDIA=1) { + !contains(QT_CONFIG, no-pkg-config):system(pkg-config --exists glib-2.0 gio-2.0 gstreamer-0.10): { + DEFINES += ENABLE_GLIB_SUPPORT=1 + PKGCONFIG += glib-2.0 gio-2.0 + } +} + +win32-*: LIBS += -lwinmm diff --git a/Source/JavaScriptCore/wtf/wtf.pro b/Source/JavaScriptCore/wtf/wtf.pro new file mode 100644 index 000000000..e59d118e2 --- /dev/null +++ b/Source/JavaScriptCore/wtf/wtf.pro @@ -0,0 +1,253 @@ +# ------------------------------------------------------------------- +# Project file for WTF +# +# See 'Tools/qmake/README' for an overview of the build system +# ------------------------------------------------------------------- + +TEMPLATE = lib +TARGET = WTF + +include(wtf.pri) + +CONFIG += staticlib + +QT += core +QT -= gui + +*-g++*:QMAKE_CXXFLAGS_RELEASE -= -O2 +*-g++*:QMAKE_CXXFLAGS_RELEASE += -O3 + +HEADERS += \ + Alignment.h \ + AlwaysInline.h \ + ArrayBuffer.h \ + ArrayBufferView.h \ + ASCIICType.h \ + Assertions.h \ + Atomics.h \ + AVLTree.h \ + Bitmap.h \ + BitVector.h \ + BloomFilter.h \ + BoundsCheckedPointer.h \ + BumpPointerAllocator.h \ + ByteArray.h \ + CheckedArithmetic.h \ + Compiler.h \ + CryptographicallyRandomNumber.h \ + CurrentTime.h \ + DateMath.h \ + DecimalNumber.h \ + Decoder.h \ + Deque.h \ + DisallowCType.h \ + dtoa.h \ + dtoa/bignum-dtoa.h \ + dtoa/bignum.h \ + dtoa/cached-powers.h \ + dtoa/diy-fp.h \ + dtoa/double-conversion.h \ + dtoa/double.h \ + dtoa/fast-dtoa.h \ + dtoa/fixed-dtoa.h \ + dtoa/strtod.h \ + dtoa/utils.h \ + DynamicAnnotations.h \ + Encoder.h \ + FastAllocBase.h \ + FastMalloc.h \ + FixedArray.h \ + Float32Array.h \ + Float64Array.h \ + Forward.h \ + Functional.h \ + GetPtr.h \ + HashCountedSet.h \ + HashFunctions.h \ + HashIterators.h \ + HashMap.h \ + HashSet.h \ + HashTable.h \ + HashTraits.h \ + HexNumber.h \ + Int16Array.h \ + Int32Array.h \ + Int8Array.h \ + ListHashSet.h \ + ListRefPtr.h \ + Locker.h \ + MainThread.h \ + MallocZoneSupport.h \ + MathExtras.h \ + MD5.h \ + MessageQueue.h \ + MetaAllocator.h \ + MetaAllocatorHandle.h \ + Noncopyable.h \ + NonCopyingSort.h \ + NotFound.h \ + NullPtr.h \ + OSAllocator.h \ + OSRandomSource.h \ + OwnArrayPtr.h \ + OwnFastMallocPtr.h \ + OwnPtr.h \ + OwnPtrCommon.h \ + PackedIntVector.h \ + PageAllocation.h \ + PageAllocationAligned.h \ + PageBlock.h \ + PageReservation.h \ + ParallelJobs.h \ + ParallelJobsGeneric.h \ + ParallelJobsLibdispatch.h \ + ParallelJobsOpenMP.h \ + PassOwnArrayPtr.h \ + PassOwnPtr.h \ + PassRefPtr.h \ + PassTraits.h \ + Platform.h \ + PossiblyNull.h \ + qt/UtilsQt.h \ + RandomNumber.h \ + RandomNumberSeed.h \ + RedBlackTree.h \ + RefCounted.h \ + RefCountedLeakCounter.h \ + RefPtr.h \ + RefPtrHashMap.h \ + RetainPtr.h \ + SHA1.h \ + Spectrum.h \ + StackBounds.h \ + StaticConstructors.h \ + StdLibExtras.h \ + StringExtras.h \ + StringHasher.h \ + TCPackedCache.h \ + TCSpinLock.h \ + TCSystemAlloc.h \ + text/ASCIIFastPath.h \ + text/AtomicString.h \ + text/AtomicStringHash.h \ + text/AtomicStringImpl.h \ + text/CString.h \ + text/StringBuffer.h \ + text/StringBuilder.h \ + text/StringConcatenate.h \ + text/StringHash.h \ + text/StringImpl.h \ + text/StringOperators.h \ + text/TextPosition.h \ + text/WTFString.h \ + Threading.h \ + ThreadingPrimitives.h \ + ThreadRestrictionVerifier.h \ + ThreadSafeRefCounted.h \ + ThreadSpecific.h \ + TypeTraits.h \ + Uint16Array.h \ + Uint32Array.h \ + Uint8Array.h \ + unicode/CharacterNames.h \ + unicode/Collator.h \ + unicode/icu/UnicodeIcu.h \ + unicode/qt4/UnicodeQt4.h \ + unicode/ScriptCodesFromICU.h \ + unicode/Unicode.h \ + unicode/UnicodeMacrosFromICU.h \ + unicode/UTF8.h \ + UnusedParam.h \ + ValueCheck.h \ + Vector.h \ + VectorTraits.h \ + VMTags.h \ + WTFThreadData.h + + +unix: HEADERS += ThreadIdentifierDataPthreads.h + +SOURCES += \ + ArrayBuffer.cpp \ + ArrayBufferView.cpp \ + Assertions.cpp \ + BitVector.cpp \ + ByteArray.cpp \ + CryptographicallyRandomNumber.cpp \ + CurrentTime.cpp \ + DateMath.cpp \ + DecimalNumber.cpp \ + dtoa.cpp \ + dtoa/bignum-dtoa.cc \ + dtoa/bignum.cc \ + dtoa/cached-powers.cc \ + dtoa/diy-fp.cc \ + dtoa/double-conversion.cc \ + dtoa/fast-dtoa.cc \ + dtoa/fixed-dtoa.cc \ + dtoa/strtod.cc \ + FastMalloc.cpp \ + gobject/GOwnPtr.cpp \ + gobject/GRefPtr.cpp \ + HashTable.cpp \ + MD5.cpp \ + MainThread.cpp \ + MetaAllocator.cpp \ + NullPtr.cpp \ + OSRandomSource.cpp \ + qt/MainThreadQt.cpp \ + qt/StringQt.cpp \ + PageAllocationAligned.cpp \ + PageBlock.cpp \ + ParallelJobsGeneric.cpp \ + RandomNumber.cpp \ + RefCountedLeakCounter.cpp \ + SHA1.cpp \ + StackBounds.cpp \ + TCSystemAlloc.cpp \ + Threading.cpp \ + TypeTraits.cpp \ + WTFThreadData.cpp \ + text/AtomicString.cpp \ + text/CString.cpp \ + text/StringBuilder.cpp \ + text/StringImpl.cpp \ + text/StringStatics.cpp \ + text/WTFString.cpp \ + unicode/CollatorDefault.cpp \ + unicode/icu/CollatorICU.cpp \ + unicode/UTF8.cpp + +unix: SOURCES += \ + OSAllocatorPosix.cpp \ + ThreadIdentifierDataPthreads.cpp \ + ThreadingPthreads.cpp + +win*|wince*: SOURCES += \ + OSAllocatorWin.cpp \ + ThreadSpecificWin.cpp \ + ThreadingWin.cpp + +*sh4* { + QMAKE_CXXFLAGS += -mieee -w + QMAKE_CFLAGS += -mieee -w +} + +lessThan(QT_GCC_MAJOR_VERSION, 5) { + # GCC 4.5 and before + lessThan(QT_GCC_MINOR_VERSION, 6) { + # Disable C++0x mode in JSC for those who enabled it in their Qt's mkspec. + *-g++*:QMAKE_CXXFLAGS -= -std=c++0x -std=gnu++0x + } + + # GCC 4.6 and after. + greaterThan(QT_GCC_MINOR_VERSION, 5) { + if (!contains(QMAKE_CXXFLAGS, -std=c++0x) && !contains(QMAKE_CXXFLAGS, -std=gnu++0x)) { + # We need to deactivate those warnings because some names conflicts with upcoming c++0x types (e.g.nullptr). + QMAKE_CFLAGS_WARN_ON += -Wno-c++0x-compat + QMAKE_CXXFLAGS_WARN_ON += -Wno-c++0x-compat + QMAKE_CFLAGS += -Wno-c++0x-compat + QMAKE_CXXFLAGS += -Wno-c++0x-compat + } + } +} diff --git a/Source/JavaScriptCore/wtf/wx/MainThreadWx.cpp b/Source/JavaScriptCore/wtf/wx/MainThreadWx.cpp new file mode 100644 index 000000000..e1d15c96f --- /dev/null +++ b/Source/JavaScriptCore/wtf/wx/MainThreadWx.cpp @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2007 Kevin Ollivier + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of Apple Computer, Inc. ("Apple") 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 APPLE AND ITS 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 APPLE OR ITS 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 "config.h" +#include "MainThread.h" + +#include <wx/defs.h> +#include <wx/app.h> +#include <wx/event.h> + +const wxEventType wxEVT_CALL_AFTER = wxNewEventType(); + +class wxCallAfter : public wxEvtHandler +{ +public: + wxCallAfter() + : wxEvtHandler() + { + wxTheApp->Connect(-1, -1, wxEVT_CALL_AFTER, wxCommandEventHandler(wxCallAfter::OnCallback)); + wxCommandEvent event(wxEVT_CALL_AFTER); + wxPostEvent(wxTheApp, event); + } + + void OnCallback(wxCommandEvent& event) + { + WTF::dispatchFunctionsFromMainThread(); + } +}; + +namespace WTF { + +void initializeMainThreadPlatform() +{ +} + +void scheduleDispatchFunctionsOnMainThread() +{ + wxCallAfter(); +} + +} // namespace WTF diff --git a/Source/JavaScriptCore/wtf/wx/StringWx.cpp b/Source/JavaScriptCore/wtf/wx/StringWx.cpp new file mode 100644 index 000000000..d5f6c578a --- /dev/null +++ b/Source/JavaScriptCore/wtf/wx/StringWx.cpp @@ -0,0 +1,84 @@ +/* + * Copyright (C) 2007 Vaclav Slavik, Kevin Ollivier <kevino@theolliviers.com> + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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 APPLE COMPUTER, INC. ``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 APPLE COMPUTER, INC. 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 "config.h" + +// The wx headers must come first in this case, because the wtf/text headers +// import windows.h, and we need to allow the wx headers to set its configuration +// first. +#include <wx/defs.h> +#include <wx/string.h> + +#include <wtf/text/CString.h> +#include <wtf/text/WTFString.h> + +namespace WTF { + +String::String(const wxString& wxstr) +{ +#if !wxUSE_UNICODE + #error "This code only works in Unicode build of wxWidgets" +#endif + +#if SIZEOF_WCHAR_T == 2 + + const UChar* str = wxstr.wc_str(); + const size_t len = wxstr.length(); + +#else // SIZEOF_WCHAR_T == 4 + + // NB: we can't simply use wxstr.mb_str(wxMBConvUTF16()) here because + // the number of characters in UTF-16 encoding of the string may differ + // from the number of UTF-32 values and we can't get the length from + // returned buffer: + +#if defined(wxUSE_UNICODE_UTF8) && wxUSE_UNICODE_UTF8 + // in wx3's UTF8 mode, wc_str() returns a buffer, not raw pointer + wxWCharBuffer wideString(wxstr.wc_str()); +#else + const wxChar *wideString = wxstr.wc_str(); +#endif + size_t wideLength = wxstr.length(); + + wxMBConvUTF16 conv; + + const size_t utf16bufLen = conv.FromWChar(0, 0, wideString, wideLength); + wxCharBuffer utf16buf(utf16bufLen); + + const UChar* str = (const UChar*)utf16buf.data(); + size_t len = conv.FromWChar(utf16buf.data(), utf16bufLen, wideString, wideLength) / 2; + +#endif // SIZEOF_WCHAR_T == 2 + + m_impl = StringImpl::create(str, len); + +} + +String::operator wxString() const +{ + return wxString(utf8().data(), wxConvUTF8); +} + +} // namespace WTF |