Source code drop for the Actionscript virtual machine in the Adobe Flash Player for December 2013
732 lines
36 KiB
C++
732 lines
36 KiB
C++
/* -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 4 -*- */
|
|
/* vi: set ts=4 sw=4 expandtab: (add to ~/.vimrc: set modeline modelines=5) */
|
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
#ifndef __avmplus_NewString__
|
|
#define __avmplus_NewString__
|
|
|
|
namespace avmplus
|
|
{
|
|
class ByteArray;
|
|
|
|
/// The utf8_t data type expressively means UTF-8 data.
|
|
typedef uint8_t utf8_t;
|
|
|
|
/**
|
|
A String can have many faces, dependent on the way the string data is stored.
|
|
The most common is the kDynamic type, meaning that the string data is owned by
|
|
the string instance. The kStatic type has the string instance point to a static buffer.
|
|
This buffer must exist as long as the string exists; character constants are
|
|
ideal candidates for this type. The kDependent type is a string that points
|
|
into another string; this type is created in a substring or concatenation operation.
|
|
Strings cannot be deleted directly, because they may be referenced be dependent strings.
|
|
<p>
|
|
String concatenation attempts first to use additional memory that the memory
|
|
allocator has left behind when a kDynamic string was allocated. If the right-hand
|
|
string fits into that buffer, the data is appended, and a dependent string is returned,
|
|
pointing to the new, larger buffer (the original string keeps its initial length
|
|
and thus does not know about the additional data). If the data does not fit, a new
|
|
kDynamic string is allocated with some extra bytes at the end, assuming that there may
|
|
be more characters to append. The minimum of extra characters is 32, then it is twice
|
|
the new length, up to a platform-dependent maximum(usually 64K). This value can be
|
|
tweaked for platforms with memory constraints in favor of more copying operations.
|
|
<p>
|
|
Strings exist in 8, 16, and 32-bit flavors. The 8-bit flavor only holds the first
|
|
256 Unicode characters. All widths ignore Unicode surrogate pairs, treating them
|
|
as ordinary characters. Use the createUTFxx() methods to deal with surrogate pairs and
|
|
UTF-8 encoding. If the kAuto type is used during creation, a quick scan is made to
|
|
see if a string would fit into a narrower width than the buffer suggests, i.e. if
|
|
a 16-bit buffer only contains 8-bit characters.
|
|
<p>
|
|
Strings cannot be deleted, since the create() methods may choose to return standard
|
|
string constants, or interned strings, or other strings that other code depends on.
|
|
*/
|
|
class String : public AvmPlusScriptableObject
|
|
{
|
|
friend class AvmCore; // for internSubstring
|
|
|
|
public:
|
|
|
|
/// String type constants. Note that isDependent() and isStatic() rely on using these values as bitflags.
|
|
enum Type
|
|
{
|
|
kDynamic = 0, // buffer is on the heap
|
|
kStatic = 1, // buffer is static
|
|
kDependent = 2 // string points into master string
|
|
};
|
|
/// String width constants.
|
|
enum Width
|
|
{
|
|
kAuto = -1, // only used in APIs
|
|
k8 = 0, // chosen such that i<<k8 == i*sizeof(uint8_t)
|
|
k16 = 1 // chosen such that i<<k16 == i*sizeof(uint16_t)
|
|
};
|
|
/**
|
|
Use this constant to define the default width for this system. If you use anything
|
|
else but kAuto, this would create strings of that width. This is not recommended.
|
|
*/
|
|
static const Width kDefaultWidth = kAuto;
|
|
|
|
/**
|
|
Create a string using Latin-1 data. Characters are just widened and copied.
|
|
To create an UTF-8 string, use createUTF8().
|
|
@param core the AvmCore instance to use
|
|
@param buffer the character buffer; if NULL, assume an empty string.
|
|
@param len the size in characters. If < 0, assume NULL termination and calculate.
|
|
@param desiredWidth the desired width; use kAuto to get a string as narrow as possible
|
|
@param staticBuf if true, the buffer is static, and may be used by the string
|
|
@return the String instance
|
|
*/
|
|
static Stringp createLatin1(AvmCore* core, const char* buffer, int32_t len = -1, Width desiredWidth = kDefaultWidth, bool staticBuf = false);
|
|
|
|
/**
|
|
Create a string using UTF-8 data. To preserve backwards compatibility, an additional
|
|
"strict" flag can be set to false. This allows a bug in the UTF-8 conversion routine
|
|
to prevail, where invalid UTF-8 sequences are copied as single characters.
|
|
@param avm the AvmCore instance to use
|
|
@param buffer the UTF-8 buffer; if NULL, assume an empty string.
|
|
@param len the size in bytes. If < 0, assume NULL termination and calculate.
|
|
@param desiredWidth the desired width; use kAuto to get a string as narrow as possible
|
|
@param staticBuf if true, the buffer is static, and may be used by the string
|
|
@param strict if false, return NULL on invalid characters (see above)
|
|
@return the String instance, or NULL on bad characters
|
|
*/
|
|
static Stringp createUTF8(AvmCore* core, const utf8_t* buffer, int32_t len = -1,
|
|
String::Width desiredWidth = String::kDefaultWidth,
|
|
bool staticBuf = false, bool strict = true);
|
|
/**
|
|
Create a string using UTF-16 data. If the desired width is too small to fit the source data,
|
|
or if the data contains an invalid surrogate pair if "strict" is true, return NULL. If
|
|
"strict" is false, invalid surrogate pairs are treated as two separate characters.
|
|
@param avm the AvmCore instance to use
|
|
@param buffer the UTF-16 buffer; if NULL, assume an empty string.
|
|
@param len the size in characters. If < 0, assume NULL termination and calculate.
|
|
@param desiredWidth the desired width; use kAuto to get a string as narrow as possible
|
|
@param staticBuf if true, the buffer is static, and may be used by the string
|
|
@param strict if false, return NULL on invalid surrogate pairs
|
|
@return the String instance, or NULL on errors
|
|
*/
|
|
static Stringp createUTF16(AvmCore* core, const wchar* buffer, int32_t len = -1,
|
|
String::Width desiredWidth = String::kDefaultWidth,
|
|
bool staticBuf = false, bool strict = true);
|
|
|
|
virtual ~String();
|
|
|
|
virtual bool gcTrace(MMgc::GC* gc, size_t cursor);
|
|
|
|
/**
|
|
Create a string with a given width out of this string. If the width is equal to the current
|
|
width, return this instance. If the desired width is too narrow to fit, or kAuto is passed
|
|
in, return NULL.
|
|
@param w the width of the new string(kAuto is not supported)
|
|
@return the String instance, or NULL on kAuto, or string too wide
|
|
*/
|
|
Stringp FASTCALL getFixedWidthString(Width w) const;
|
|
/**
|
|
Returns the Atom equivalent of this String. This is
|
|
done by or'ing the proper type bits into the pointer.
|
|
*/
|
|
REALLY_INLINE Atom atom() const { return Atom(AtomConstants::kStringType | uintptr_t(this)); }
|
|
/**
|
|
virtual version of atom():
|
|
*/
|
|
virtual Atom toAtom() const { return atom(); }
|
|
/**
|
|
If this string is a static or dependent string, make it dynamic so the static
|
|
data can be released. The string is only
|
|
made dynamic if the static string pointer falls within the given data buffer.
|
|
This prevents unnecessary dynamization of static strings if the string data
|
|
belongs to a different data buffer.
|
|
*/
|
|
void makeDynamic(const uint8_t* dataStart, uint32_t dataSize);
|
|
/**
|
|
Check the master of this string if this is a dependent string. If there is
|
|
any indication that using this string would hold a lock on a big master
|
|
string, create a dynamic string and release the master string. The AvmCore
|
|
uses this method to ensure that interned strings do not keep a lock on a huge
|
|
master string if the master string is dynamic, trying to limit the waste of
|
|
memory.
|
|
*/
|
|
void fixDependentString();
|
|
/**
|
|
Produce a has code of this string.
|
|
*/
|
|
int32_t hashCode() const;
|
|
/**
|
|
Use the same algorithm to produce a hash code for Latin1 data.
|
|
*/
|
|
static int32_t FASTCALL hashCodeLatin1(const char* buf, int32_t len);
|
|
/**
|
|
Use the same algorithm to produce a hash code for UTF-16 data.
|
|
*/
|
|
static int32_t FASTCALL hashCodeUTF16(const wchar* buf, int32_t len);
|
|
|
|
/**
|
|
Use the same algorithm to produce hash code for string corresp to value.
|
|
*/
|
|
static int32_t FASTCALL hashCodeUInt(uint32_t value);
|
|
|
|
/// Return the length in characters.
|
|
REALLY_INLINE int32_t length() const { return m_length; }
|
|
// overload used by AS3 glue code.
|
|
int get_length() const { return m_length; }
|
|
/// Is this string empty?
|
|
REALLY_INLINE bool isEmpty() const { return m_length == 0; }
|
|
/// Return the width constant.
|
|
REALLY_INLINE Width getWidth() const { return Width(m_bitsAndFlags & TSTR_WIDTH_MASK); }
|
|
/// Return the string type.
|
|
REALLY_INLINE int32_t getType() const { return ((m_bitsAndFlags & TSTR_TYPE_MASK) >> TSTR_TYPE_SHIFT); }
|
|
/// Return true iff getType() == kDependent.
|
|
REALLY_INLINE bool isDependent() const { return (m_bitsAndFlags & (kDependent << TSTR_TYPE_SHIFT)) != 0; }
|
|
/// Return true iff getType() == kStatic.
|
|
REALLY_INLINE bool isStatic() const { return (m_bitsAndFlags & (kStatic << TSTR_TYPE_SHIFT)) != 0; }
|
|
/// Is this an interned string?
|
|
REALLY_INLINE bool isInterned() const { return (m_bitsAndFlags & TSTR_INTERNED_FLAG) != 0; }
|
|
/// Mark this string as interned.
|
|
REALLY_INLINE void setInterned() { m_bitsAndFlags |= TSTR_INTERNED_FLAG; }
|
|
/**
|
|
Return the character at the given position. No index checks!
|
|
@param index the index
|
|
@return the character at the index
|
|
*/
|
|
wchar FASTCALL charAt(int32_t index) const;
|
|
|
|
/**
|
|
Compare the String with toCompare. If the length is > 0, compare
|
|
the other string up to the given length.
|
|
@param other the string to compare with
|
|
@param start the starting position (in other)
|
|
@param length the length to compare (if > 0) (in other)
|
|
@return = 0 if the strings are identical,
|
|
< 0 if this string is less than toCompare,
|
|
> 0 if this string is greater than toCompare
|
|
*/
|
|
int32_t FASTCALL Compare(String& other, int32_t other_start = 0, int32_t other_length = 0) const;
|
|
|
|
/**
|
|
Compare this string with another string.
|
|
*/
|
|
bool FASTCALL equals(Stringp that) const;
|
|
|
|
/**
|
|
Compare one string with another string where each one could be null.
|
|
This returns an int32_t to work directly with the JIT
|
|
*/
|
|
static int32_t FASTCALL equalsWithNullChecks(Stringp s1, Stringp s2);
|
|
|
|
/**
|
|
Compare this string with a Latin1 string.
|
|
*/
|
|
bool FASTCALL equalsLatin1(const char* p, int32_t len = -1) const;
|
|
/**
|
|
Compare this string with a UTF-16 string.
|
|
*/
|
|
bool FASTCALL equalsUTF16(const wchar* p, int32_t len) const;
|
|
|
|
/*@{*/
|
|
/**
|
|
* Returns the length of str, in # of characters. If the length does not
|
|
* fit in an int32_t, these functions do not return.
|
|
*/
|
|
static int32_t FASTCALL Length(const wchar* str);
|
|
static int32_t FASTCALL Length(const char* str);
|
|
/*@}*/
|
|
|
|
/**
|
|
Implements String.indexOf().
|
|
*/
|
|
int32_t FASTCALL indexOf(Stringp s, int32_t offset = 0) const;
|
|
|
|
/**
|
|
Convenience method for old code (boolean result)
|
|
*/
|
|
REALLY_INLINE bool contains(Stringp s) const { return indexOf(s) >= 0; }
|
|
/**
|
|
Convenience method: indexOf() for a Latin-1 string within a given range.
|
|
@param p the character string to compare; NULL returns -1
|
|
@param len the number of characters to compare; if < 0, call Length()
|
|
@param start the starting position
|
|
@param end the ending position
|
|
@return the index of the found position, or -1 if no match
|
|
*/
|
|
int32_t FASTCALL indexOfLatin1(const char* p, int32_t len = -1, int32_t start = 0, int32_t end = 0x7FFFFFFF) const;
|
|
|
|
/**
|
|
Convenience method: indexOf() for a string of length 1.
|
|
@param c the character code to compare
|
|
@param start the starting position
|
|
@param end the ending position
|
|
@return the index of the found position, or -1 if no match
|
|
*/
|
|
int32_t FASTCALL indexOfCharCode(wchar c, int32_t start = 0, int32_t end = 0x7FFFFFFF) const;
|
|
/**
|
|
Convenience method for old code (boolean result)
|
|
*/
|
|
REALLY_INLINE bool containsLatin1(const char* p) const { return indexOfLatin1(p) >= 0; }
|
|
|
|
/**
|
|
Convenience method: Does a Latin-1 string match at the current position?
|
|
@param p the character string to compare; NULL returns false
|
|
@param len the number of characters to compare; if < 0, call Length()
|
|
@param pos the position to match
|
|
@return true if the string matches
|
|
*/
|
|
bool FASTCALL matchesLatin1(const char* p, int32_t len, int32_t pos);
|
|
|
|
/**
|
|
Convenience method: Does a Latin-1 string match at the current position, ignoring case?
|
|
@param p the character string to compare; NULL returns false
|
|
@param len the number of characters to compare; if < 0, call Length()
|
|
@param pos the position to match
|
|
@return true if the string matches
|
|
*/
|
|
bool FASTCALL matchesLatin1_caseless(const char* p, int32_t len, int32_t pos);
|
|
/**
|
|
Implements String.lastIndexOf().
|
|
*/
|
|
int32_t FASTCALL lastIndexOf(Stringp s, int32_t offset = 0x7fffffff) const;
|
|
/**
|
|
Concatenate two strings, and return the result. If the right string fits into the buffer
|
|
end of the left string, append the data and return a new dependent string pointing
|
|
to that buffer. If it does not fit, create a kDynamic string containing the entire
|
|
buffer, with extra padding at the end to support in-place concatenation.
|
|
@param left the left string; may be NULL
|
|
@param right the right string; may be NULL, although not meaningful
|
|
@return the concatenated string
|
|
*/
|
|
static Stringp FASTCALL concatStrings(Stringp leftStr, Stringp rightStr);
|
|
/**
|
|
Append a String instance.
|
|
@param src the string to append
|
|
@return the concatenated string
|
|
*/
|
|
Stringp FASTCALL append(Stringp str);
|
|
|
|
/*
|
|
Append a 8-bit-wide string. For Unicode, strings should be Latin1, not UTF8.
|
|
*/
|
|
REALLY_INLINE Stringp appendLatin1(const char* p) { return _append(NULL, Pointers((const uint8_t*)p), Length(p), k8); }
|
|
REALLY_INLINE Stringp appendLatin1(const char* p, int32_t len) { return _append(NULL, Pointers((const uint8_t*)p), len, k8); }
|
|
/*
|
|
Append a 16-bit-wide string. For Unicode, strings should be UTF16, but this is not enforced
|
|
by this method: indeed, several callers expect to be able to create "illegal" UTF16 sequences
|
|
via this call, for backwards compatibility. Thus, this is a dangerous call and should be used with
|
|
caution (and is also the reason it is not named "appendUTF16").
|
|
*/
|
|
REALLY_INLINE Stringp append16(const wchar* p) { return _append(NULL, Pointers(p), Length(p), k16); }
|
|
REALLY_INLINE Stringp append16(const wchar* p, int32_t len) { return _append(NULL, Pointers(p), len, k16); }
|
|
/**
|
|
Implement String.substr(). The resulting String object points into the original string,
|
|
and holds a reference to the original string.
|
|
*/
|
|
Stringp FASTCALL substr(int32_t start, int32_t len = 0x7fffffff);
|
|
/**
|
|
Implement String.substring(). The resulting String object points into the original string,
|
|
and holds a reference to the original string.
|
|
*/
|
|
Stringp FASTCALL substring(int32_t start, int32_t end = 0x7fffffff);
|
|
/**
|
|
* Make a copy in the target core.
|
|
*/
|
|
Stringp clone(AvmCore* targetCore);
|
|
|
|
/**
|
|
Implement String.slice(). The resulting String object points into the original string,
|
|
and holds a reference to the original string.
|
|
*/
|
|
Stringp FASTCALL slice(int32_t start, int32_t end);
|
|
/**
|
|
This routine is a very specific parser to generate a positive integer from a string.
|
|
The following are supported:
|
|
"0" - one single digit for zero - NOT "00" or any other form of zero
|
|
[1-9]+[0-9]* up to 2^32-2(4294967294)
|
|
2^32-1(4294967295) is not supported(see ECMA quote below).
|
|
The ECMA that we're supporting with this routine is...
|
|
cn: the ES3 test for a valid array index is
|
|
"A property name P(in the form of a string value) is an array index if and
|
|
only if ToString(ToUint32(P)) is equal to P and ToUint32(P) is not equal to 2^32-1."
|
|
Don't support 000000 as 0.
|
|
We don't support 0x1234 as 1234 in hex since string(1234) doesn't equal '0x1234')
|
|
No leading zeros are supported
|
|
*/
|
|
bool FASTCALL parseIndex(uint32_t& result) const;
|
|
/**
|
|
Returns a new string object which is a copy of this string object, with all
|
|
characters in the string converted to uppercase.
|
|
Unicode character classes for uppercase and lowercase are used. The conversion
|
|
behavior is compliant with the String.toUpperCase method. The method returns
|
|
this instance if no changes were detected.
|
|
@return the resulting string or NULL
|
|
*/
|
|
Stringp FASTCALL toUpperCase();
|
|
/**
|
|
Returns a new string object which is a copy of this string object, with all
|
|
characters in the string converted to lowercase.
|
|
Unicode character classes for uppercase and lowercase are used. The conversion
|
|
behavior is compliant with the String.toUpperCase method. The method returns
|
|
this instance if no changes were detected.
|
|
@return the resulting string or NULL
|
|
*/
|
|
Stringp FASTCALL toLowerCase();
|
|
/**
|
|
Change the case of a string according to the case mapper supplied.
|
|
If no changes were detected, return this instance, otherwise, return
|
|
a new instance.
|
|
@param unimapper the mapping function to call
|
|
@return the changed string
|
|
*/
|
|
Stringp FASTCALL caseChange(uint32_t(*unimapper)(uint32_t));
|
|
/**
|
|
Returns a kIntptrType Atom if the string holds an integer that fits into
|
|
such an atom. For use in our ScriptObject HashTable implementation. If we
|
|
have a valid integer equivalent, it will never be zero since kIntptrType tag != 0.
|
|
*/
|
|
Atom FASTCALL getIntAtom() const;
|
|
/**
|
|
This conversion handles hex, octal, base 10 integer, float, and "Infinity"/"-Infinity".
|
|
*/
|
|
double toNumber();
|
|
/**
|
|
Check if this character is a valid space character.
|
|
*/
|
|
static bool isSpace(wchar ch);
|
|
/**
|
|
Is this string all whitespace?
|
|
*/
|
|
bool isWhitespace() const;
|
|
|
|
/// Native functions, used by StringClass.cpp
|
|
int _indexOf(Stringp s, int i=0);
|
|
int AS3_indexOf(Stringp s, double i=0);
|
|
|
|
int _lastIndexOf(Stringp s, int i=0x7fffffff);
|
|
int AS3_lastIndexOf(Stringp s, double i=0x7fffffff);
|
|
|
|
Stringp _charAtI(int32_t i);
|
|
Stringp _charAtU(uint32_t i);
|
|
Stringp AS3_charAt(double i=0);
|
|
|
|
double _charCodeAtDI(int32_t i); // returns NaN for out-of-bounds
|
|
int32_t _charCodeAtII(int32_t i); // returns 0 for out-of-bounds
|
|
double AS3_charCodeAt(double i); // returns NaN for out-of-bounds
|
|
int32_t _charCodeAtID(double i); // returns 0 for out-of-bounds
|
|
double _charCodeAtDU(uint32_t i); // returns NaN for out-of-bounds
|
|
int32_t _charCodeAtIU(uint32_t i); // returns 0 for out-of-bounds
|
|
|
|
int32_t AS3_localeCompare(Atom other);
|
|
|
|
Stringp _substring(int i_start, int i_count);
|
|
Stringp AS3_substring(double d_start, double d_count);
|
|
|
|
Stringp _slice(int dStart, int dEnd);
|
|
Stringp AS3_slice(double dStart, double dEnd);
|
|
|
|
Stringp _substr(int dStart, int dEnd);
|
|
Stringp AS3_substr(double dStart, double dEnd);
|
|
|
|
Stringp AS3_toUpperCase();
|
|
Stringp AS3_toLowerCase();
|
|
|
|
// Useful utilities used by the core code.
|
|
static wchar wCharToUpper(wchar ch) { return (wchar) unicharToUpper(ch); }
|
|
static wchar wCharToLower(wchar ch) { return (wchar) unicharToLower(ch); }
|
|
static uint32_t unicharToUpper(uint32_t ch);
|
|
static uint32_t unicharToLower(uint32_t ch);
|
|
#ifdef DEBUGGER
|
|
virtual uint64_t bytesUsed() const;
|
|
virtual void registerDependentObjects(IMemorySampler *) { }
|
|
Stringp getMasterString() const; // used by profiler
|
|
#endif
|
|
|
|
PrintWriter& print(PrintWriter& prw) const;
|
|
|
|
private:
|
|
friend class StringIndexer;
|
|
friend class StUTF8String;
|
|
friend class StUTF16String;
|
|
friend class CodegenLIR;
|
|
|
|
private:
|
|
/**
|
|
This is a union of three different pointers, or an offset value -- you
|
|
must know what type of String this is (static, dynamic, dependent) to know
|
|
how to interpret the field.
|
|
|
|
Note that the offset value is always in bytes, regardless of string width!
|
|
|
|
*** WARNING ***
|
|
This struct is only used inside of String itself, and should not ever be allocated on the stack.
|
|
If you want to obtain a pointer to the start of the string's character buffer, the simplest
|
|
and safest way to do this is to use the Pointers struct (see below).
|
|
*/
|
|
struct Buffer
|
|
{
|
|
union
|
|
{
|
|
void* pv;
|
|
uint8_t* p8;
|
|
wchar* p16;
|
|
uintptr_t offset_bytes;
|
|
};
|
|
REALLY_INLINE explicit Buffer(const void* _pv) { pv = const_cast<void*>(_pv); }
|
|
REALLY_INLINE explicit Buffer(uintptr_t _offset_bytes) { offset_bytes = _offset_bytes; }
|
|
};
|
|
|
|
/**
|
|
Extra storage, for the Master pointer (for dependent strings)
|
|
or index value (lazily calculated for other strings)
|
|
*/
|
|
struct Extra
|
|
{
|
|
union
|
|
{
|
|
Stringp master; // used for dependent strings
|
|
mutable uint32_t index; // if not dependent, this is the index value for getIntAtom/parseIndex
|
|
};
|
|
REALLY_INLINE explicit Extra(Stringp _master) { master = _master; }
|
|
};
|
|
|
|
// ------------------------ DATA SECTION BEGIN
|
|
Buffer m_buffer; // buffer pointer (dynamic, static, or offset into master)
|
|
Extra m_extra;
|
|
int32_t m_length; // length in characters
|
|
mutable uint32_t m_bitsAndFlags; // various bits and flags, see below (must be unsigned)
|
|
// ------------------------ DATA SECTION END
|
|
|
|
enum {
|
|
TSTR_WIDTH_MASK = 0x00000001, // string width (right-aligned for fast access)
|
|
TSTR_TYPE_MASK = 0x00000006, // type index, 2 bits
|
|
TSTR_TYPE_SHIFT = 1,
|
|
// If TSTR_7BIT_FLAG is set, the string has width of k8, with no characters having the high bit set.
|
|
// Thus the string is both 7-bit ascii and "utf8-compatible" as-is; knowing this can produce
|
|
// huge speedups in code that uses utf8 conversion heavily (in conjunction with "ascii" strings, of course).
|
|
// Note that this bit is set lazily (and currently, only by StUTF8String), thus, if this bit is clear,
|
|
// the string might still be 7-bit-ascii... we just haven't checked yet.
|
|
TSTR_7BIT_FLAG = 0x00000008,
|
|
TSTR_7BIT_SHIFT = 3,
|
|
TSTR_INTERNED_FLAG = 0x00000010, // this string is interned
|
|
TSTR_NOINT_FLAG = 0x00000020, // set in getIntAtom() if the string is not an 28-bit integer
|
|
TSTR_NOUINT_FLAG = 0x00000040, // set in parseIndex() if the string is not an unsigned integer
|
|
TSTR_UINT28_FLAG = 0x00000080, // set if m_index contains value for getIntAtom()
|
|
TSTR_UINT32_FLAG = 0x00000100, // set if m_index contains value for parseIndex()
|
|
TSTR_CHARSLEFT_MASK = 0xFFFFFE00, // characters left in buffer field (for inplace concat)
|
|
TSTR_CHARSLEFT_SHIFT = 9
|
|
};
|
|
|
|
/**
|
|
This is a TEMPORARY struct, always stack-allocated, that is used to extract the current starting
|
|
pointer for a string. It may look superficially similar to the "Buffer" struct, but is different,
|
|
in that the pointer is always correct (unlike Buffer, which might actually be an offset into a master).
|
|
|
|
WARNING: this struct is not valid across a GC collection; if you do anything that could trigger
|
|
a collection (most commonly, any sort of allocation), you must assume the contents of this are invalid.
|
|
*/
|
|
struct Pointers
|
|
{
|
|
union
|
|
{
|
|
void* pv;
|
|
uint8_t* p8;
|
|
wchar* p16;
|
|
};
|
|
REALLY_INLINE explicit Pointers(const String* const self)
|
|
{
|
|
AvmAssert(self != NULL);
|
|
if (!self->isDependent())
|
|
p8 = self->m_buffer.p8;
|
|
else
|
|
p8 = self->m_extra.master->m_buffer.p8 + self->m_buffer.offset_bytes;
|
|
}
|
|
REALLY_INLINE explicit Pointers(const uint8_t* _p8) { p8 = const_cast<uint8_t*>(_p8); }
|
|
REALLY_INLINE explicit Pointers(const uint16_t* _p16) { p16 = const_cast<uint16_t*>(_p16); }
|
|
};
|
|
|
|
REALLY_INLINE void setType(char index) { m_bitsAndFlags = (m_bitsAndFlags & ~TSTR_TYPE_MASK) |(index << TSTR_TYPE_SHIFT); }
|
|
REALLY_INLINE int32_t getCharsLeft() const { return (m_bitsAndFlags & TSTR_CHARSLEFT_MASK) >> TSTR_CHARSLEFT_SHIFT; }
|
|
REALLY_INLINE void setCharsLeft(int32_t n) { m_bitsAndFlags = (m_bitsAndFlags & ~TSTR_CHARSLEFT_MASK) |(n << TSTR_CHARSLEFT_SHIFT); }
|
|
|
|
// Create a string with no buffer.
|
|
static Stringp createDependent(MMgc::GC* gc, Stringp master, int32_t start, int32_t len);
|
|
// Create a string with a dynamic buffer.
|
|
static Stringp createDynamic(MMgc::GC* gc, const void* data, int32_t len, Width w, bool is7bit, int32_t extra=0);
|
|
// Create a string with a static buffer.
|
|
static Stringp createStatic(MMgc::GC* gc, const void* data, int32_t len, Width w, bool is7bit);
|
|
|
|
// Convert the string data to a dynamic buffer.
|
|
void convertToDynamic();
|
|
|
|
/**
|
|
Low-level append worker.
|
|
*/
|
|
Stringp _append(Stringp rightStrPtr, const Pointers& rightStr, int32_t numChars, Width width);
|
|
|
|
#ifdef _DEBUG
|
|
void verify7bit() const;
|
|
//#define VERIFY_7BIT(s) do { if (s) (s)->verify7bit(); } while (0)
|
|
// extremely slow, so disabled by default, even in debug mode.
|
|
#define VERIFY_7BIT(s) do { } while (0)
|
|
#else
|
|
#define VERIFY_7BIT(s) do { } while (0)
|
|
#endif
|
|
|
|
/**
|
|
Make operator new private - people should use the create functions
|
|
*/
|
|
REALLY_INLINE void* operator new(size_t size, MMgc::GC *gc, MMgc::GCExactFlag)
|
|
{
|
|
return AvmPlusScriptableObject::operator new(size, gc, MMgc::kExact);
|
|
}
|
|
REALLY_INLINE void operator delete(void*) {} // Strings cannot be deleted
|
|
|
|
// ctor for a static string.
|
|
REALLY_INLINE String(const void* buffer, Width w, int32_t length, bool is7bit);
|
|
// ctor for a dynamic string.
|
|
REALLY_INLINE String(MMgc::GC* gc, void* buffer, Width w, int32_t length, int32_t charsLeft, bool is7bit);
|
|
// ctor for a dependent string.
|
|
REALLY_INLINE String(MMgc::GC* gc, Stringp master, int32_t start, int32_t length);
|
|
};
|
|
|
|
// Compare helpers
|
|
REALLY_INLINE bool operator==(String& s1, String& s2)
|
|
{
|
|
return s1.equals(&s2);
|
|
}
|
|
REALLY_INLINE bool operator!=(String& s1, String& s2)
|
|
{
|
|
return !s1.equals(&s2);
|
|
}
|
|
REALLY_INLINE bool operator<(String& s1, String& s2)
|
|
{
|
|
return s2.Compare(s1) < 0;
|
|
}
|
|
REALLY_INLINE bool operator>(String& s1, String& s2)
|
|
{
|
|
return s2.Compare(s1) > 0;
|
|
}
|
|
REALLY_INLINE bool operator<=(String& s1, String& s2)
|
|
{
|
|
return s2.Compare(s1) <= 0;
|
|
}
|
|
REALLY_INLINE bool operator>=(String& s1, String& s2)
|
|
{
|
|
return s2.Compare(s1) >= 0;
|
|
}
|
|
|
|
REALLY_INLINE /*static*/ bool String::isSpace(wchar ch)
|
|
{
|
|
const uint32_t IS_SPACE_MASK =
|
|
(1U << (32-1)) | // space
|
|
(1U << (9-1)) | // tab
|
|
(1U << (10-1)) | // LF
|
|
(1U << (13-1)); // CR
|
|
|
|
ch -= 1;
|
|
return (ch < 32) & // bitwise and, *not* logical and -- avoids a branch
|
|
((IS_SPACE_MASK & (1U<<ch)) != 0);
|
|
}
|
|
|
|
/**
|
|
The StringIndexer class provides quick access to single characters by index.
|
|
Use an instance of this class on the stack if multiple index access is required.
|
|
Previously, this class allowed for faster access, as the interior pointer
|
|
didn't need to be recalculated; later changes made this unsafe, so this class
|
|
exists mostly for legacy purposes at this point.
|
|
*/
|
|
|
|
class StringIndexer
|
|
{
|
|
public:
|
|
/// The constructor takes the string to index.
|
|
REALLY_INLINE explicit StringIndexer(Stringp s) : m_str(s) { AvmAssert(s != NULL); }
|
|
/// Return the embedded string.
|
|
REALLY_INLINE String* operator->() const { return m_str; }
|
|
REALLY_INLINE String* str() const { return m_str; }
|
|
/// Quick index operator.
|
|
REALLY_INLINE wchar operator[](int index) const { return m_str->charAt(index); }
|
|
|
|
private:
|
|
Stringp const m_str;
|
|
|
|
// do not create on the heap
|
|
void* operator new(size_t); // unimplemented
|
|
void operator delete(void*); // unimplemented
|
|
};
|
|
|
|
/**
|
|
The StUTF8String class is simply a data buffer containing 0-terminated UTF-8 data.
|
|
The instance can only be created on the stack to preserve the data buffer during GC.
|
|
Note that the length() function returns the length not including the 0-terminator.
|
|
Also note that the string might contain interior NULL characters (if the original
|
|
String did) and thus String::Length, strlen, etc might return misleading values.
|
|
*/
|
|
|
|
class StUTF8String
|
|
{
|
|
public:
|
|
explicit StUTF8String(Stringp str);
|
|
~StUTF8String();
|
|
REALLY_INLINE const char* c_str() const { return m_buffer; }
|
|
REALLY_INLINE int32_t length() const { return m_length; }
|
|
private:
|
|
// do not create on the heap
|
|
void* operator new(size_t); // unimplemented
|
|
void operator delete(void*); // unimplemented
|
|
private:
|
|
const char* m_buffer;
|
|
int32_t m_length;
|
|
};
|
|
|
|
/**
|
|
The StUTF16String class is simply a data buffer containing 0-terminated UTF-16 data.
|
|
The instance can only be created on the stack to preserve the data buffer during GC.
|
|
Note that the length() function returns the length not including the 0-terminator.
|
|
Also note that the string might contain interior NULL characters (if the original
|
|
String did) and thus String::Length, strlen, etc might return misleading values.
|
|
If the string is a 32-bit string, characters > 0xFFFF are converted to surrogate pairs.
|
|
*/
|
|
|
|
class StUTF16String
|
|
{
|
|
public:
|
|
explicit StUTF16String(Stringp str);
|
|
~StUTF16String();
|
|
REALLY_INLINE const wchar* c_str() const { return m_buffer; }
|
|
REALLY_INLINE int32_t length() const { return m_length; }
|
|
private:
|
|
const wchar* m_buffer;
|
|
int32_t m_length;
|
|
// do not create on the heap
|
|
void* operator new(size_t); // unimplemented
|
|
void operator delete(void*); // unimplemented
|
|
};
|
|
|
|
class StIndexableUTF8String : public StUTF8String
|
|
{
|
|
private:
|
|
int32_t m_lastPos;
|
|
int32_t m_lastUtf8Pos;
|
|
bool m_indexable;
|
|
|
|
public:
|
|
|
|
explicit StIndexableUTF8String(Stringp s);
|
|
|
|
/**
|
|
Convert a string index to an UTF-8 index.
|
|
Return the original index if out of range.
|
|
*/
|
|
int32_t FASTCALL toUtf8Index(int32_t pos);
|
|
|
|
/**
|
|
Convert an UTF-8 index to a string index.
|
|
Return the original index if < 0.
|
|
*/
|
|
int32_t FASTCALL toIndex(int32_t uf8Pos);
|
|
};
|
|
|
|
}
|
|
|
|
#endif // __avmplus_NewString__
|